The Callback Hell argument is weak as there is nothing in the pattern forcing you to write ten levels of callbacks nested as anonymous functions inline, but the pattern does make it easy for you as a developer to do so.
Promises is a coming ECMAScript 6 specification that’s extracted from open source libraries providing this functional approach to solving control flow for async code. The concept behind promises is that when returning a Promise you are returning an object that promises to either successfully resolve with a value or to reject with a reason.
Promises are sometimes called thenables, referring to one of the most important parts of the specification stating that every Promise must have a method then that takes one or two functions as arguments, a resolvedHandler and a rejectedHandler. Every such handler must then return a new Promise — often handled by the library for you as well — thus achieving chaining: promiseMe().then().then().then(resolved).
Some of the important benefits promises brings to the callback-table are that they
… don’t force me to rewrite all of my code
The nice thing about promises is that they don’t interfere with a functions arguments but rather strictly adheres to arguments as input and return values as output. Callbacks are notoriously bad because they conflate inputs and outputs for a function. Promises are also interchangeable so instead of passing a callback 5 levels deeper you can just return the promise in every function — even changing it on the way — before you grab it from the entry point.
It is true that some code at some point has to change to create the promises, but most changes can be kept at wrapping function calls.
… will compose
Promises are composable so you can achieve g(f(x)), an impossible feat using callbacks. The syntax changes so this isn’t perfect either, but supporting square(square(2)) would require more cruft in the square function.
Lets compare this with how we would solve this out of the box with callbacks:
Lets take the Promise composition example and adapt it to use an existing sync square method to prove that we can wrap existing code into promises/thenables:
Promises correctly bubbles exception upwards, aligning Promise based code with its synchronous counterpart. Look at this callback implementation, it’s obviously convoluted and brittle:
Lets write the same code using Promises:
Promises uses plain normal exceptions for its error handling, meaning we automatically can work with built-ins like JSON.parse in this example.
Pitfalls of Promises
Wrapping node core API
Unfortunately we need to fight Node.js when going pure Promises. It introduces some complexity but promises libraries help us out with wrapper methods:
I’m in a romantic relationship with async.js
You’re in luck! Most things you thought async kicked ass at, Promises pretty much one-ups! Lets take something a little “complex” — the auto example for async.js and the same written using Promises:
Having said so, async.js is still an awesome library with a ton of great control flow helpers.
Promises are slow!
This used to be a valid argument but with the Bluebird Promise library the arguments moot. Gorgi Kosev shared an analysis of async patterns where Bluebird is almost as fast as plain callbacks, and over twice as fast as async.js. The same is the case for memory usage.
Get me some Promises
There’s a lot of Promises implementations popping up, but I will recommend three concrete ones:
- Q.js — One of the older and more mature libraries. Heavily used and feature rich
- RSVP.js — Written for Ember by Yehuda Katz
- Bluebird.js — My favourite library. Unmatched performance
I would also recommend you to learn more from people smarter than me who has written about Promises: