Lesson Tuesday

Promises are one of the most powerful new features in ES6. A promise allows us to wrap async code and then wait for the result of that code before moving on. In this lesson, we’ll discuss why promises are so useful and how we can use them to tidy up our async code.

Promises have been a key concept in JavaScript development for quite some time, but up until ES6, they weren’t native to JS. Instead, developers relied on promise libraries like Bluebird.js or used jQuery’s then() method. Some developers still prefer to use promise libraries instead of ES6’s native functionality because these libraries have more features or run more quickly than ES6 promises.

We’ve already learned how to use callbacks to ensure our code runs in order. But what happens when we need to chain many async and sync functions together? Here’s an abstracted example of what that might look like using pseudocode:

doAsync(function() {
  doAsync2(function() {
    doAsync3(function() {
      doAsync4(function() {
        doAsync5(function() {
          // return something here.
        });
      });
    });
  });
});

Here we have callback after callback, each nested inside the previous one. This is known as the pyramid of doom because of the pyramid-like shape of the indented code. It’s also known as callback hell. It’s difficult to read and reason about. It’s also prone to bugs.

Promises were created in order to deal with this issue. With promises, we could write the above code more like this (once again, we’re using pseudocode):

doAsync()
  .then doAsync()
  .then doAsync2()
  .then doAsync3()
  .then doAsync4()
  .then doAsync5()

Promises make it much easier to chain together async functions and read and reason about our code. So how can we incorporate them into our code?

In the pseudocode example above, we use the method .then(), which returns a Promise object. A promise can have three states:

  • Pending: The object's initial state. A pending operation has been started, but not completed yet.
  • Fulfilled: A promise is fulfilled when the operation has been successfully completed.
  • Rejected: A promise is rejected means when the operation fails for some reason.

Imagine you’re waiting to renew your driver’s license at the DMV. When you go in, you get a piece of paper with a number on it. You wait until your number is called and then you go to the counter to renew your license.

That piece of paper is similar to a promise. It represents an appointment you’ll have in the future, but that appointment doesn’t exist yet. While you’re waiting for your number to be called, the promise is pending. The promise will either be fulfilled (driver’s license renewed...yay!) or rejected (better study for the driving test more…)

Once the promise is either fulfilled or rejected, it is complete and becomes immutable. An immutable value can’t be changed. Our promise also can’t be used again, just as you can’t use a number to get back in line at the DMV after your appointment is finished.

With this in mind, we're ready to use promises to return the result of an API call.