Understanding JavaScript Promises
JavaScript Promises have been growing in popularity for several years, and have finally become a part of the JavaScript specification! Promises are easy to understand, and make it easy to write asynchronous code.
A simple explanation of a Promise
A Promise makes it easy to write code that waits for a background task to finish, and then continues once the result is ready.
This happens by calling promise.then(...)
:
// Let's start by getting a "user" object over the network:
var promiseForAUser = getUserFromTheNetwork();
// We don't have a "user" yet! We have a "Promise for a user".
// So, we must wait for the background work to finish:
promiseForAUser.then(function(user) {
// And now we can continue!
// The background work is finished,
// and the result was a "user"
})
What’s so great about Promises?
JavaScript applications do a lot of work in the background – especially network communication. And there are several ways to wait for background work to finish. Events and callbacks are very common approaches, and they’ve been around for a long time.
But Promises have 2 features that make them such a powerful solution:
- Promises are easy to chain and nest. This makes it easy to compose several operations, run things sequentially and in parallel, and wait for everything to finish before continuing.
- Errors will automatically bubble-up in a Promise chain. This means your code can focus on the “happy path”, but still ensure errors will be caught!
All this power comes from a single function
All the power of a Promise sits in the .then()
method. This method has 4 important features:
-
It lets you wait for the value
var userPromise = getUserAsync(); // Waiting for the `user`... userPromise.then(function(user) { // Continue: alert(user.name); });
``
-
It creates the next link in the chain. Whatever you return from a callback will be sent to the next link. This means you can transform the result of the Promise:
getUserAsync().then(function(user) { // The next link will get this return value: return user.name; }).then(function(name) { alert(name); });
``
-
If you return a Promise from the callback – something special happens – the next link waits for this Promise too! This means your Promises can be nested and depend on each other:
getUserAsync() // Wait for the first promise: .then(function(user) { // Return a nested promise: return getProfileAsync(user.id); }) // Wait for both promises to finish: .then(function(profile) { console.log(profile); });
``
-
It lets you catch errors from anywhere in the chain, by passing a callback as the second parameter:
getUserAsync() .then(function successHandler(user) { throw new Error("Oh no, some problem with " + user.name); }) .then(function() { // This function will be skipped, because of the Error. }) .then(function() { // This function will be skipped too. }, function _catch(err) { // Any errors in the chain will be caught here! // This not only includes the `throw new Error` line above, // but would also include any errors from `getUserAsync()` alert("Failed to retrieve user! " + err.message); // Rethrow the error, to continue "bubbling-up" through the rest of the Promise chain: throw err; });
`` This behaves much like a
try { ... } catch (err) { ... }
block:- An error, whether it’s a connection error or any thrown
Error
, will stop normal execution of the Promise chain. - Errors “bubble-up” through the rest of the Promise chain, unless an error handler is found.
- The error handler has the same features as the success handler:
- If the error handler rethrows an error, then the error will continue to “bubble-up” through the remainder of the Promise chain.
- If the error handler returns successfully, then the Promise chain will continue normal execution.
- If the error handler returns a value, the value will be sent to the next link.
- If the error handler returns a Promise, the next link waits for this Promise too.
- An error, whether it’s a connection error or any thrown
There are lots of different Promise libraries out there, with more methods than just .then()
, but ultimately all those methods are just utilities that all rely on .then()
.
That’s it!
Now that you understand how simple a Promise is, stay tuned - in my next article I’ll share 8 tips for mastering promises.