Hacker News new | comments | show | ask | jobs | submit login
Sane Async Patterns in JavaScript (slideshare.net)
65 points by hijonathan 1300 days ago | hide | past | web | 28 comments | favorite

  Next time you're about to define a function with a callback argument, don't.
No, do. It's the prevailing style in Node.js and elsewhere, and so there exist tools to transform them (syntactically or otherwise) into more palpable styles:

https://github.com/0ctave/node-sync https://github.com/BYVoid/continuation https://github.com/JeffreyZhao/wind https://github.com/Sage/streamlinejs

I already knew about node-sync, but continuation really brings something new. I'll check the source to understand what it does under the hood.

Also, the comparison on the project's README is informative (and lists additional options):


I agree, I often will write promise wrappers for other people's stuff, but for the external surface of my module I have callbacks.

Also, callbacks ARE sometimes nicer than promises, etc... especially if you have CoffeeScript function syntax.

It's an interesting presentation, but it neglects an obvious solution to the nested callbacks problem, which are the various "async"-like control-flow libraries out there. More importantly, it neglects a critical problem with all of these approaches, which is being able to observe asynchronous state (from a debugger, repl, or whatever) when things go wrong -- when a callback (or event) you expected doesn't fire, or the same one is called (or emitted) early or too many times. IME, these are the really hard problems to debug in async JavaScript, and there are improvements available (I wrote node-vasync for this), but I still wouldn't say it's well solved by any of these approaches.

Agreed. I think I saw that async in C# has exceptions that behave as one naively expects they should. That would make JS so much more awesome, even with callbacks. The worst part of them isn't the pyramid, IMO, so much as they all the horrible repeated "if error...."

"Nested spaghetti" strikes me as a mixed metaphor. I'd have to ask an expert on Italian cuisine, but I don't think spaghetti nests. Literary aspects aside, I don't think the "pyramid of doom" is what is meant by spaghetti. In the pyramid, it's easy to see the sequence in which things happen. In spaghetti code, it's hard to see the sequence -- that's the problem.

The fact that the pyramid makes it easy to see the sequence, i.e. follow the chain of causation, and easy to find the callbacks, argues that there is a place for it. Additionally, it makes it easy to transfer state through the chain, since each callback has access to the previous one's variables.

The other patterns, which probably all have appropriate uses, too, would seem to carry a greater risk of spaghetti -- i.e. control that jumps around unexpectedly and is hard to follow.

It's easy to see what's happening if everything is written in one file. It's hard if your callback are fired from and/or go into minimized library code you don't control or don't want to debug. You may have your own pyramid, but it's really sitting on someone else's pyramid.

Yeah, the real problem for me with the nested case is error handling. It's all manual, and the default (unless you're very careful) is that errors do not propagate and will instead simply be lost and ignored.

This "PubSub" pattern is really trying to recreate RxJS but without the expressiveness - if you find yourself doing this kind of work a lot, you should check it out



PubSub is pre-node, making RxJS a "more expressive" PubSub.

Dear title-editing moderators: the "in javascript" part was relevant.

These patterns apply to Javascript, which I wasn't expecting from the title. Mods, could you edit in "Javascript" somewhere in the title, please?

IME, most clutter from async logic in JavaScript, however it's dressed up, only happens at all because of implementing something that is fundamentally sequential and synchronous using the wrong tools for the job. The slide "A JS-er's lament" was a great demonstration of how fundamentally broken the current JS model is.

Put another way, the obviously missing pattern to me is to just move the concurrency up to the level of having multiple threads, and then use synchronous interfaces for synchronous operations. Now that Web Workers are starting to become more widely supported, I don't understand why we aren't heading for synchronous APIs as fast as humanly possible.

The bit about AMD didn't really fit in with the rest. Why start talking about module loading when you were discussing async earlier? They are completely separate issues.

Have you tried state machines?

My JS programming often looks something like: https://gist.github.com/anonymous/5314313

I don't really care if the APIs I use have callbacks or promises, both fit in just fine.

Nice example :) I tend to think state machines are underused, they give you wonderfully predictable behavior.

The sooner we get Generator support everywhere the better so we can start using http://taskjs.org/ and stop talking about these issues.

While looking through the slides, I was wondering:

Is there some community-standard way of naming/declaring a JS function to let the programmer know just by reading the function's name that it will return a promise instead of a final computed value? I'm interested in knowing how people handle this. It's easy to forget that a function doesn't return a promise if the function name makes no mention of it, so I just make it explicit in the function name... but that's just how I handle it.

I'm getting in the habit of letting the caller decide, something like the following:

    myAsyncFunc = function (param1, param2, callback) {
      var usingPromises = (callback === undefined);
      var returnVal = usingPromises ? Q.defer() : this;

      someFunction(function (err, res) {
        if (usingPromises) {
          err ? returnVal.reject(err) : returnVal.resolve(res);
        } else {
          callback(err, res);

      return returnVal;
So if I call myAsyncFunc(foo, bar) it returns a promise and if I call myAsyncFunc(foo, bar, cb) it returns itself for chaining.

Functions should be documented with the types of their parameters and return value.

I suppose I've just learned to really appreciate the "?" and "!" at the end of ruby function names...

If that function is "supposed" to be async and its last argument is not something looking like "callback", there's a great chance it's a Promise.

Otherwise, there's no "naming scheme" for promise-based functions.

Promises are about as clean as you can get in a purely async environment.

Yet, notice how no one shows off how easy it is to write a loop. Oh, and what happens when your code throws an exception? It's as though that path halted forever.

Also, what happens when you have a method defined as

result = foo(args);

that now needs to perform an async operation? Enjoy refactoring every call site.

Sometimes require.js feels a bit heavy for what is mostly a straightforward concept (AMD), so I use a much simplified version of the pattern: https://gist.github.com/potch/4156519

I'm new to js and using async.js on the client and node server. Is that acceptable? Honestly I just dont find promises very intuitive.

That twitter quote is pretty funny.

slideshare.net is blocked at the office.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact