Promises

Introducing and understanding ES6 Promises.

Scroll down...

Content

Resources

Comments

Promises take asynchronous programming in JavaScript to a new and much more organized level. This lesson covers what promises are, why they were created and how to use them.

What are Promises?

Promise pinky swear

The Promise object is used for asynchronous computations. A Promise represents a value which may be available now, or in the future, or never.

A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.

It's kind of awkward to think of something called a promise as representing a value that we never know if it will ever exist. However, that is not the real beauty of promises.

Promises exist to solve the problem presented by "callback hell" and to provide a standardized interface for responding to asynchronous events.

A promise may have one of 3 states:

  • Pending - The initial state of a promise.
  • Fulfilled - The state of a promise representing a successful operation.
  • Rejected - The state of a promise representing a failed operation.

It is important to note that the state of a promise is immutable once it has been resolved or rejected. That means it will not change.

Creating a Promise

Creating a promise is fairly simple. A promise can be instantiated with the new keyword. The constructor takes a callback that accepts two parameters. The first is resolve and the second is reject. Here is an example:

var p = new Promise(function(resolve, reject) {
  // Do something asynchronous
});

Once you've created a promise, you can use the resolve and reject parameters to pass the return values from your successful or failing asynchronous operations.

Using resolve and reject

The resolve and reject parameters passed to your function are also functions. When you call them, you pass in the value that you want to be returned when the promise succeeds and fails respectively. Let's take a look at how this works.

The following code creates a promise that checks if the current time in milliseconds is even or odd. If it is even, it resolves the string 'Even' and if not, it rejects with the string 'Odd'.

var testEven = new Promise(function(resolve, reject) {
  if (Date.now() % 2 === 0) {
    resolve('Even');
  } else {
    reject('Odd');
  }
});

Using then

Once you have a promise you'll want to access its result. We can do that by chaining a call to then on the promise object. By default then takes up to two parameters. The first being the callback when the promise is resolved and the second being an optional callback if the promise is rejected.

Here is an example using then to output the result of the promise we created above:

testEven.then(function(result) {
  console.log(result);
}, function(err) {
  console.error(err);
});

Using catch

You can think of catch as a promise that only is concerned with rejections and errors. It gets its name from the keyword catch that is used in try {} catch(e) {} blocks. Notice here that it can actually replace the second parameter that handles the rejection:

testEven
  .then(function(result) {
    console.log(result);
  })
  .catch(function(err) {
    console.error(err);
  });

Another important note is that catch also catches errors when they are thrown and outputs the error message. So even if the promise resolves and there is an error thrown in the resolve call, the chained catch will be invoked here and output "Boom!".

testEven
  .then(function(result) {
    console.log(result);
    throw "Boom!";
  })
  .catch(function(err) {
    console.error(err); //=> Boom!
  });

Using Promise.all

If you have multiple promises that you want to resolve as a collection Promise.all takes care of this for you with the same simple then syntax. All that you have to do is pass the collection of promises to Promise.all and chain then as you would with a normal promise. The result passed to your then callback will be a collection with the results of each promise in its corresponding location.

var nums = [1, 2, 3, 4];
nums = nums.map(function(i) {
  return Promise.resolve(i + 1);
});

Promise.all(nums)
  .then(function(results) {
    console.log(results); //=> [ 2, 3, 4, 5 ]
  });

Code Review

Here are the important snippets of code from this lesson.

// Contruct a promise
var testEven = new Promise(function(resolve, reject) {
  if (Date.now() % 2 === 0) {
    resolve('Even');
  } else {
    reject('Odd');
  }
});

// Call then on a promise
testEven.then(function(result) {
  console.log(result);
}, function(err) {
  console.error(err);
});

// Call catch
testEven
  .then(function(result) {
    console.log(result);
  })
  .catch(function(err) {
    console.error(err);
  });


testEven
  .then(function(result) {
    console.log(result);
    throw "Boom!";
  })
  .catch(function(err) {
    console.error(err); //=> Boom!
  });

// Using Promise.all
var nums = [1, 2, 3, 4];
nums = nums.map(function(i) {
  return Promise.resolve(i + 1);
});

Promise.all(nums)
  .then(function(results) {
    console.log(results); //=> [ 2, 3, 4, 5 ]
  });

Wrapping Up

The use cases and power of promises know no bounds. Literally, everything that is asynchronous can be wrapped into promises. The more promises are used, the more standardized your code will be. So embrace them, because they are the present and future!



Sign up to track your progress for free

There are ( ) additional resources for this lesson. Check them out!

Sorry, comments aren't active just yet!

Next Lesson: Using the Node Debugger