Promises. Right?

There are things about them that just seem, you know, natural once you've been using them for long enough. But they may be a little hard to grok if you're new to them.

Mind blown.

So here’s my short and opinionated list of what you should know about JavaScript promises (roughly in order of importance). Here we go.

  • Avoid them. Yeah you heard me. Use coroutines instead. In IQ-test-speak, coroutines are to promises what promises are to callbacks. They're simpler to use, they unbreak your return and they make your code shorter, more readable and yeah, full of rainbows. That said, JS coroutines yield promises so you should have an understanding of how they work. Read this or this. Or this.

  • Use Bluebird. It’s currently the best available promise library for JS, period. Sorry, Q.

  • Promises infect code (in a good way). Once you have a promise, you generally don’t need to create another promise. Just use the one you already have. In other words don’t do this:

iReturnAPromise()  
  .then(function (){
    return new Promise(function (resolve, reject) {
      resolve(iReturnAPromiseToo());
    });
  });

but rather chain promises (see below).

  • Promises chain. That’s the main reason to use them - they’re composable. A then handler returns a promise (as does catch) so you can call another then on it and so on and so forth. Like so:
iReturnAPromise()  
  .then(function (){
    var foo = iAmSynchronous();
    return iReturnAPromiseToo(foo);
  })
  .then(function (result) {
    // Do something cool with result of iReturnAPromiseToo.
  })
  • When you think of promises error handling, think about handling (synchronous) exceptions. It's very similar. Catch exceptions (errors) in a catch handler. Important thing: any exception thrown in a promise handler will be "translated" to a rejected promise. Common practice is to include a “catch all” handler at the end of the promise chain to handle all uncaught errors.

  • If you want to explicitly end your promise chain (so that noone can chain it further), use .done() at the end (this will also throw any unhandled rejected promises as exceptions).

  • Returning a resolved promise from a .catch handler means you "recovered" from the caught error and it will result in calling the next chained .then handler. More often then not you don't want to do that.

  • Yes, doing an “asynchronous if” in promise handlers sucks. No, there’s no nice way around it. Unless you use coroutines (see point #1).

  • If you get a promise from a 3rd party library, wrap it in Promise.resolve(externalPromise) to transform it into a bluebird promise if you want to call bluebird specific functions or if you want to return it “upstream”. If you just "bubble" it through to the next handler in your chain, you're fine.

  • If you want to log in your promise chain, tap into it.

  • You cannot modify a promise in finally handler. finally is pretty much for side effects only. If you want to return a promise for the same value both on failure and success, you'll need to do that explicitly in a then and a catch handler.

  • If you want to share state between handlers, use .bind({}) and assign the state to this. It's better than sharing state via closure. Just make sure you're not using ES6 arrow function syntax (=>) for handlers in that case (it binds this lexically which would break the whole thing).

For further tips and/or more detailed explanations, I very much recommend reading through Bluebird's excellent documentation, it really is full of gold.

Enjoy asynchronous JavaScript!

What are your promise tips and tricks? I'm @tomasbrambora!