Promises

Learn about promises, promises/A+, and thenables and how they work, and also the concept of promisification in Node.js.

Promises are part of the ECMAScript 2015 standard (or ES6, which is why they’re also called ES6 promises) and have been natively available in Node.js since version 4. But the history of promises goes back a few years earlier, when there were dozens of implementations around, initially with different features and behaviors. Eventually, the majority of those implementations settled on a standard called Promises/A+.

Promises represent a big step ahead toward providing a robust alternative to continuation-passing style callbacks for propagating an asynchronous result. As we’ll see, the use of promises will make all the major asynchronous control flow constructs easier to read, less verbose, and more robust compared to their callback-based alternatives.

What’s a Promise?

A Promise is an object that embodies the eventual result (or error) of an asynchronous operation. In promises jargon, we say that a Promise is pending when the asynchronous operation is not yet complete, it’s fulfilled when the operation successfully completes and rejected when the operation terminates with an error. Once a Promise is either fulfilled or rejected, it’s considered settled.

To receive the fulfillment value or the error (reason) associated with the rejection, we can use the then() method of a Promise instance. The following is its signature:

promise.then(onFulfilled, onRejected)

In the preceding signature, onFulfilled is a callback that’ll eventually receive the fulfillment value of the Promise, and onRejected is another callback that’ll receive the reason for the rejection (if any). Both are optional.

To have an idea of how promises can transform our code, let’s consider the following callback-based code:

asyncOperation(arg, (err, result) => {
if(err) {
// handle the error
}
// do stuff with the result
})

Promises allow us to transform this typical continuation-passing style code into a better structured and more elegant code, such as the following:

asyncOperationPromise(arg)
.then(result => {
// do stuff with result
}, err => {
// handle the error
})

In the code above, asyncOperationPromise() is returning a Promise, which we can then use to receive the fulfillment value or the rejection reason of the eventual result of the function. So far, it seems that there’s nothing major going on, but one crucial property of the then() method is that it synchronously returns another Promise.

Moreover, if any of the onFulfilled or onRejected functions return a value xx, the Promise returned by the then() method will:

  • Fulfill with xx ...