Get insightful engineering articles delivered directly to your inbox.
By

— 4 minute read

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:

  1. 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.
  2. 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:

  1. It lets you wait for the value

    var userPromise = getUserAsync();
    // Waiting for the `user`...
    userPromise.then(function(user) {
        // Continue:
        alert(user.name);
    });
    

    ``

  2. 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);
    });
    

    ``

  3. 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);
    });
    

    ``

  4. 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.

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.

By
Scott is a Sr. Front End Engineer at InVision.

Like what you've been reading? Join us and help create the next generation of prototyping and collaboration tools for product design teams around the world. Check out our open positions.