Promises are an alternative to callbacks for delivering the results of an asynchronous computation. They require more effort from implementors of asynchronous functions, but provide several benefits for users of those functions.
The following function returns a result asynchronously, via a Promise:
asyncFunc() as follows:
then() always returns a Promise, which enables you to chain method calls:
How the Promise P returned by
then() is settled depends on what its callback does:
Furthermore, note how
catch() handles the errors of two asynchronous function calls (
asyncFunction2()). That is, uncaught errors are passed on until there is an error handler.
If you chain asynchronous function calls via
then(), they are executed sequentially, one at a time:
If you don’t do that and call all of them immediately, they are basically executed in parallel (a fork in Unix process terminology):
Promise.all() enables you to be notified once all results are in (a join in Unix process terminology). Its input is an Array of Promises, its output a single Promise that is fulfilled with an Array of the results.
The Promise API is about delivering results asynchronously. A Promise object (short: Promise) is a stand-in for the result, which is delivered via that object.
Reacting to state changes:
then(), to be notified of a fulfillment or a rejection.
then()method. Whenever the API is only interested in being notified of settlements, it only demands thenables (e.g. the values returned from
catch(); or the values handed to
Changing states: There are two operations for changing the state of a Promise. After you have invoked either one of them once, further invocations have no effect.
Promises are a pattern that helps with one particular kind of asynchronous programming: a function (or method) that returns a single result asynchronously. One popular way of receiving such a result is via a callback (“callbacks as continuations”):
Promises provide a better way of working with callbacks: Now an asynchronous function returns a Promise, an object that serves as a placeholder and container for the final result. Callbacks registered via the Promise method
then() are notified of the result:
Compared to callbacks as continuations, Promises have the following advantages:
then()returns a Promise (e.g. the result of calling another Promise-based function) then
then()returns that Promise (how this really works is more complicated and explained later). As a consequence, you can chain
Let’s look at a first example, to give you a taste of what working with Promises is like.
With Node.js-style callbacks, reading a file asynchronously looks like this:
With Promises, the same functionality is used like this:
There are still callbacks, but they are provided via methods that are invoked on the result (
catch()). The error callback in line B is convenient in two ways: First, it’s a single style of handling errors (versus
if (error) and
try-catch in the previous example). Second, you can handle the errors of both
readFilePromisified() and the callback in line A from a single location.
The code of
readFilePromisified() is shown later.
Let’s look at three ways of understanding Promises.
The following code contains a Promise-based function
asyncFunc() and its invocation.
asyncFunc() returns a Promise. Once the actual result
'DONE' of the asynchronous computation is ready, it is delivered via
resolve() (line B), which is a parameter of the callback that starts in line A.
So what is a Promise?
asyncFunc()is a blocking function call.
The following code invokes
asyncFunc() from the async function
main(). Async functions are a feature of ECMAScript 2017.
The body of
main() expresses well what’s going on conceptually, how we usually think about asynchronous computations. Namely,
asyncFunc() is a blocking function call:
Prior to ECMAScript 6 and generators, you couldn’t suspend and resume code. That’s why, for Promises, you put everything that happens after the code is resumed into a callback. Invoking that callback is the same as resuming the code.
If a function returns a Promise then that Promise is like a blank into which the function will (usually) fill in its result, once it has computed it. You can simulate a simple version of this process via an Array:
With Promises, you don’t access the eventual value via
 (as in line A), you use method
then() and a callback.
Another way to view a Promise is as an object that emits events.
Registering the event listener (line B) can be done after calling
asyncFunc(), because the callback handed to
setTimeout() (line A) is executed asynchronously (after this piece of code is finished).
Normal event emitters specialize in delivering multiple events, starting as soon as you register.
In contrast, Promises specialize in delivering exactly one value and come with built-in protection against registering too late: the result of a Promise is cached and passed to event listeners that are registered after the Promise was settled.
Let’s look at how Promises are operated from the producer and the consumer side.
As a producer, you create a Promise and send a result via it:
Once a result was delivered via a Promise, the Promise stays locked in to that result. That means each Promise is always in either one of three (mutually exclusive) states:
A Promise is settled (the computation it represents has finished) if it is either fulfilled or rejected. A Promise can only be settled once and then stays settled. Subsequent attempts to settle have no effect.
The parameter of
new Promise() (starting in line A) is called an executor:
resolve(). That usually fulfills the Promise
p. But it may not – resolving with a Promise
qis still pending then so is
pwill be settled the same way.
reject(). That always rejects the Promise.
If an exception is thrown inside the executor,
p is rejected with that exception.
As a consumer of
promise, you are notified of a fulfillment or a rejection via reactions – callbacks that you register with the methods
What makes Promises so useful for asynchronous functions (with one-off results) is that once a Promise is settled, it doesn’t change anymore. Furthermore, there are never any race conditions, because it doesn’t matter whether you invoke
catch() before or after a Promise is settled:
catch() is simply a more convenient (and recommended) alternative to calling
then(). That is, the following two invocations are equivalent:
A Promise library has complete control over whether results are delivered to Promise reactions synchronously (right away) or asynchronously (after the current continuation, the current piece of code, is finished). However, the Promises/A+ specification demands that the latter mode of execution be always used. It states so via the following requirement (2.2.4) for the
onRejectedmust not be called until the execution context stack contains only platform code.
That means that your code can rely on run-to-completion semantics (as explained in the previous chapter) and that chaining Promises won’t starve other tasks of processing time.
Additionally, this constraint prevents you from writing functions that sometimes return results immediately, sometimes asynchronously. This is an anti-pattern, because it makes code unpredictable. For more information, consult “Designing APIs for Asynchrony” by Isaac Z. Schlueter.
Before we dig deeper into Promises, let’s use what we have learned so far in a few examples.
The following code is a Promise-based version of the built-in Node.js function
readFilePromisified() is used like this:
The following is a Promise-based function that performs an HTTP GET via the event-based XMLHttpRequest API:
This is how you use
setTimeout() as the Promise-based function
delay() (similar to
Note that in line A, we are calling
resolve with zero parameters, which is the same as calling
resolve(undefined). We don’t need the fulfillment value in line B, either and simply ignore it. Just being notified is enough here.
Note that the rejection after the timeout (in line A) does not cancel the request, but it does prevent the Promise being fulfilled with its result.
timeout() looks like this:
Now we are ready to dig deeper into the features of Promises. Let’s first explore two more ways of creating Promises.
Promise.resolve(x) works as follows:
x, it returns a Promise that is fulfilled with
xis a Promise whose constructor is the receiver (
Promiseif you call
xis returned unchanged:
xis a thenable, it is converted to a Promise: the settlement of the thenable will also become the settlement of the Promise. The following code demonstrates that.
fulfilledThenablebehaves roughly like a Promise that was fulfilled with the string
'hello'. After converting it to the Promise
then()works as expected (last line).
That means that you can use
Promise.resolve() to convert any value (Promise, thenable or other) to a Promise. In fact, it is used by
Promise.race() to convert Arrays of arbitrary values to Arrays of Promises.
Promise.reject(err) returns a Promise that is rejected with
In this section, we take a closer look at how Promises can be chained. The result of the method call:
is a new Promise Q. That means that you can keep the Promise-based control flow going by invoking
then() on Q:
onRejectedthrow an exception.
If you resolve the Promise Q returned by
then() with a normal value, you can pick up that value via a subsequent
You can also resolve the Promise Q returned by
then() with a thenable R. A thenable is any object that has a method
then() that works like
Promise.prototype.then(). Thus, Promises are thenables. Resolving with R (e.g. by returning it from
onFulfilled) means that it is inserted “after” Q: R’s settlement is forwarded to Q’s
onRejected callbacks. In a way, Q becomes R.
The main use for this mechanism is to flatten nested
then() calls, like in the following example:
The flat version looks like this:
Whatever you return in an error handler becomes a fulfillment value (not rejection value!). That allows you to specify default values that are used in case of failure:
Exceptions that are thrown in the callbacks of
catch() are passed on to the next error handler, as rejections:
There can be one or more
then() method calls that don’t have error handlers. Then the error is passed on until there is an error handler.
In the following code, a chain of two Promises is built, but only the first part of it is returned. As a consequence, the tail of the chain is lost.
This can be fixed by returning the tail of the chain:
If you don’t need the variable
promise, you can simplify this code further:
In the following code, the invocation of
asyncFunc2() is nested:
The fix is to un-nest this code by returning the second Promise from the first
then() and handling it via a second, chained,
In the following code, method
insertInto() creates a new Promise for its result (line A):
If you look closely, you can see that the result Promise is mainly used to forward the fulfillment (line C) and the rejection (line D) of the asynchronous method call
db.insert() (line B).
The fix is to not create a Promise, by relying on
then() and chaining:
resultCode(line B) and let
then()create the Promise for us.
then()will pass on any rejection produced by
then()for error handling
catch(cb) is an abbreviation for
then(null, cb). But using both parameters of
then() at the same time can cause problems:
The rejection callback (line D) receives all rejections of
asyncFunc1(), but it does not receive rejections created by the fulfillment callback (line A). For example, the synchronous function call in line B may throw an exception or the asynchronous function call in line C may produce a rejection.
Therefore, it is better to move the rejection callback to a chained
In programs, there are two kinds of errors:
For operational errors, each function should support exactly one way of signaling errors. For Promise-based functions that means not mixing rejections and exceptions, which is the same as saying that they shouldn’t throw exceptions.
For programmer errors, it can make sense to fail as quickly as possible, by throwing an exception:
If you do this, you must make sure that your asynchronous code can handle exceptions. I find throwing exceptions acceptable for assertions and similar things that could, in theory, be checked statically (e.g. via a linter that analyzes the source code).
If exceptions are thrown inside the callbacks of
catch() then that’s not a problem, because these two methods convert them to rejections.
However, things are different if you start your async function by doing something synchronous:
If an exception is thrown in line A then the whole function throws an exception. There are two solutions to this problem.
You can catch exceptions and return them as rejected Promises:
You can also start a chain of
then() method calls via
Promise.resolve() and execute the synchronous code inside a callback:
An alternative is to start the Promise chain via the Promise constructor:
This approach saves you a tick (the synchronous code is executed right away), but it makes your code less regular.
Sources of this section:
Composing means creating new things out of existing pieces. We have already encountered sequential composition of Promises: Given two Promises P and Q, the following code produces a new Promise that executes Q after P is fulfilled.
Note that this is similar to the semicolon for synchronous code: Sequential composition of the synchronous operations
g() looks as follows.
This section describes additional ways of composing Promises.
Let’s assume you want to perform two asynchronous computations,
asyncFunc2() in parallel:
The two function calls
asyncFunc2() are made without
then() chaining. As a consequence, they are both executed immediately and more or less in parallel. Execution is now forked; each function call spawned a separate “thread”. Once both threads are finished (with a result or an error), execution is joined into a single thread in either
The problem with this approach is that it involves too much manual and error-prone work. The fix is to not do this yourself, by relying on the built-in method
Promise.all(iterable) takes an iterable over Promises (thenables and other values are converted to Promises via
Promise.resolve()). Once all of them are fulfilled, it fulfills with an Array of their values. If
iterable is empty, the Promise returned by
all() is fulfilled immediately.
One nice thing about Promises is that many synchronous tools still work, because Promise-based functions return results. For example, you can use the Array method
promisedTexts is an Array of Promises. We can use
Promise.all(), which we have already encountered in the previous section, to convert that Array to a Promise that fulfills with an Array of results.
Promise.race(iterable) takes an iterable over Promises (thenables and other values are converted to Promises via
Promise.resolve()) and returns a Promise P. The first of the input Promises that is settled passes its settlement on to the output Promise. If
iterable is empty then the Promise returned by
race() is never settled.
As an example, let’s use
Promise.race() to implement a timeout:
This section describes two useful methods for Promises that many Promise libraries provide. They are only shown to further demonstrate Promises, you should not add them to
Promise.prototype (this kind of patching should only be done by polyfills).
When you chain several Promise method calls, you risk silently discarding errors. For example:
then() in line A produces a rejection, it will never be handled anywhere. The Promise library Q provides a method
done(), to be used as the last element in a chain of method calls. It either replaces the last
then() (and has one to two arguments):
Or it is inserted after the last
then() (and has zero arguments):
Quoting the Q documentation:
The Golden Rule of
thenusage is: either return your promise to someone else, or if the chain ends with you, call
doneto terminate it. Terminating with
catchis not sufficient because the catch handler may itself throw an error.
This is how you would implement
done() in ECMAScript 6:
done’s functionality is clearly useful, it has not been added to ECMAScript 6. The idea was to first explore how much engines can detect automatically. Depending on how well that works, it may to be necessary to introduce
Sometimes you want to perform an action independently of whether an error happened or not. For example, to clean up after you are done with a resource. That’s what the Promise method
finally() is for, which works much like the
finally clause in exception handling. Its callback receives no arguments, but is notified of either a resolution or a rejection.
This is how
Domenic Denicola proposes to implement
The callback determines how the settlement of the receiver (
this) is handled:
finally(). In a way, we take
finally()out of the chain of methods.
Example 1 (by Jake Archibald): using
finally() to hide a spinner. Simplified version:
Example 2 (by Kris Kowal): using
finally() to tear down a test.
The Promise library Q has tool functions for interfacing with Node.js-style
(err, result) callback APIs. For example,
denodeify converts a callback-based function to a Promise-based one:
denodify is a micro-library that only provides the functionality of
Q.denodeify() and complies with the ECMAScript 6 Promise API.
There are many Promise libraries out there. The following ones conform to the ECMAScript 6 API, which means that you can use them now and easily migrate to native ES6 later.
Larger Promise libraries:
Q.Promiseby Kris Kowal implements the ES6 API.
ES6 standard library polyfills:
Implementing asynchronous functions via Promises is more convenient than via events or callbacks, but it’s still not ideal:
In line A, execution blocks (waits) via
yield until the result of
Promise.all() is ready. That means that the code looks synchronous while performing asynchronous operations.
Details are explained in the chapter on generators.
In this section, we will approach Promises from a different angle: Instead of learning how to use the API, we will look at a simple implementation of it. This different angle helped me greatly with making sense of Promises.
The Promise implementation is called
DemoPromise. In order to be easier to understand, it doesn’t completely match the API. But it is close enough to still give you much insight into the challenges that actual implementations face.
DemoPromise is a class with three prototype methods:
reject are methods (versus functions handed to a callback parameter of the constructor).
Our first implementation is a stand-alone Promise with minimal functionality:
then(). It must work independently of whether the Promise has already been settled or not.
This is how this first implementation is used:
The following diagram illustrates how our first
then() first. It has to handle two cases:
onRejected, to be used when the Promise is settled.
onRejectedcan be invoked right away.
The previous code snippet uses the following helper function:
resolve() works as follows: If the Promise is already settled, it does nothing (ensuring that a Promise can only be settled once). Otherwise, the state of the Promise changes to
'fulfilled' and the result is cached in
this.promiseResult. Next, all fulfillment reactions, that have been enqueued so far, are be triggered.
reject() is similar to
The next feature we implement is chaining:
then()returns a Promise that is resolved with what either
onRejectedare missing, whatever they would have received is passed on to the Promise returned by
then() creates and returns a new Promise (lines A and F). Additionally,
rejectedTask are set up differently: After a settlement…
onFulfilledis used to resolve
onFulfilledis missing, we use the fulfillment value to resolve
onRejectedis used to resolve (not reject!)
onRejectedis missing, we use pass on the rejection value to
Flattening is mostly about making chaining more convenient: Normally, returning a value from a reaction passes it on to the next
then(). If we return a Promise, it would be nice if it could be “unwrapped” for us, like in the following example:
We returned a Promise in line A and didn’t have to nest a call to
then() inside the current method, we could invoke
then() on the method’s result. Thus: no nested
then(), everything remains flat.
We implement this by letting the
resolve() method do the flattening:
We can make flattening more generic if we allow Q to be a thenable (instead of only a Promise).
To implement locking-in, we introduce a new boolean flag
this.alreadyResolved. Once it is true,
this is locked and can’t be resolved anymore. Note that
this may still be pending, because its state is now the same as the Promise it is locked in on.
The actual resolution now happens in the private method
The flattening is performed in line A: If
value is fulfilled, we want
self to be fulfilled and if
value is rejected, we want
self to be rejected. The forwarding happens via the private methods
_doReject, to get around the protection via
With chaining, the states of Promises become more complex (as covered by Sect. 25.4 of the ECMAScript 6 specification):
If you are only using Promises, you can normally adopt a simplified worldview and ignore locking-in. The most important state-related concept remains “settledness”: a Promise is settled if it is either fulfilled or rejected. After a Promise is settled, it doesn’t change, anymore (state and fulfillment or rejection value).
If you want to implement Promises then “resolving” matters, too and is now harder to understand:
As our final feature, we’d like our Promises to handle exceptions in user code as rejections. For now, “user code” means the two callback parameters of
The following excerpt shows how we turn exceptions inside
onFulfilled into rejections – by wrapping a
try-catch around its invocation in line A.
If we wanted to turn
DemoPromise into an actual Promise implementation, we’d still need to implement the revealing constructor pattern : ES6 Promises are not resolved and rejected via methods, but via functions that are handed to the executor, the callback parameter of the constructor.
If the executor throws an exception then “its” Promise must be rejected.
One important advantage of Promises is that they will increasingly be used by asynchronous browser APIs and unify currently diverse and incompatible patterns and conventions. Let’s look at two upcoming Promise-based APIs.
The fetch API is a Promise-based alternative to XMLHttpRequest:
fetch() returns a Promise for the actual request,
text() returns a Promise for the content as a string.
The ECMAScript 6 API for programmatically importing modules is based on Promises, too:
Compared to events, Promises are better for handling one-off results. It doesn’t matter whether you register for a result before or after it has been computed, you will get it. This advantage of Promises is fundamental in nature. On the flip side, you can’t use them for handling recurring events. Chaining is another advantage of Promises, but one that could be added to event handling.
Compared to callbacks, Promises have cleaner function (or method) signatures. With callbacks, parameters are used for input and output:
With Promises, all parameters are used for input:
Additional Promise advantages include:
Promises work well for for single asynchronous results. They are not suited for:
ECMAScript 6 Promises lack two features that are sometimes useful:
This section gives an overview of the ECMAScript 6 Promise API, as described in the specification.
The constructor for Promises is invoked as follows:
The callback of this constructor is called an executor. The executor can use its parameters to resolve or reject the new Promise
xis thenable, its settlement is forwarded to
p(which includes triggering reactions registered via
pis fulfilled with
pwith the value
e(often an instance of
The following two static methods create new instances of their receivers:
Promise.resolve(x): converts arbitrary values to Promises, with an awareness of Promises.
xis the receiver,
xis returned unchanged.
Promise.reject(reason): creates a new instance of the receiver that is rejected with the value
Intuitively, the static methods
Promise.race() compose iterables of Promises to a single Promise. That is:
The methods are:
Promise.all(iterable): returns a Promise that…
Promise.race(iterable): the first element of
iterablethat is settled is used to settle the returned Promise.
onRejectedare called reactions.
onFulfilledis called immediately if the Promise is already fulfilled or as soon as it becomes fulfilled. Similarly,
onRejectedis informed of rejections.
then()returns a new Promise Q (created via the species of the constructor of the receiver):
onFulfilledhas been omitted, a fulfillment of the receiver is forwarded to the result of
onRejectedhas been omitted, a rejection of the receiver is forwarded to the result of
Default values for omitted reactions could be implemented like this:
p.catch(onRejected)is the same as
 “The Revealing Constructor Pattern” by Domenic Denicola (this pattern is used by the