A quick guide to JavaScript Promises
Tags: javascript, promises, async, callbacks
Published: 2017-06-12 13:19:19

---
cover_image: https://thepracticaldev.s3.amazonaws.com/i/67x7ofkc7h2zv9f3a9ih.png
title: A quick guide to JavaScript Promises
published: true
description: Asynchronous code with callbacks in JavaScript can quickly get you into callback hell. With Promises we can avoid this. Learn how to use them.
tags: javascript, promises, async, callbacks
canonical_url: https://www.twilio.com/blog/2016/10/guide-to-javascript-promises.html
---

When you are writing JavaScript, [callbacks](https://developer.mozilla.org/en-US/docs/Mozilla/js-ctypes/Using_js-ctypes/Declaring_and_Using_Callbacks#Using_Callbacks) are one of the most confusing concepts. [Promises](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) are the new approach to improve working with async code.

![Decorative GIF](https://www.twilio.com/blog/wp-content/uploads/2016/09/31orCejQRkSvmchYeZC2GKswNtst-d_xEoSPoP3X-bAm9RRe8hxz59vVZrrRm78VvJgVbuUo5R7dAikR2gY1rxtqQ14yMJP8K4CS3Siiir_wRpB6IYgoWGlpokE51vV4eYAI2lpP-1.png)

One of the biggest problems of callbacks is the chaining of different asynchronous activities. You end up calling anonymous function after function to pass around values. The result is an unmaintainable “callback hell”. Promises try to solve this problem but can be a bit confusing in the beginning.

Let’s define what Promises are, why they are incredibly useful and how to do things like executing calls in parallel or in series. For this we will look at different examples of doing HTTP requests using two different Node.js libraries.

## Setup

Before we get started you need the following things to try our code examples:
- [Node.js](https://nodejs.org/en/) version 6.0 or higher. You can check your version by running `node -v` in your command line. You can upgrade by downloading the latest binary from [their website](https://nodejs.org/en/download/) or by using a tool like [nvm](https://github.com/creationix/nvm).

Once you have this, you need to create a new folder. I’ll create a `promises` folder in my home directory. Install the [`fetch`](https://www.npmjs.com/package/node-fetch) and [`request`](https://www.npmjs.com/package/request) libraries by running the following command in that folder:

```bash
npm install node-fetch request --save
```

Create a new file called `promises.js` and place the following lines in there to load the library:

```js
const fetch = require('node-fetch');
const request = require('request');
```

We will be working out of the same `promise.js` file throughout the whole post.

## Quick Promise Overview

To understand the benefits of Promises let’s first look at how to do an asynchronous call without promises. For this we will do an HTTP GET request using the `request` library.

Add the following lines to `promises.js`:

```js
request.get('http://httpbin.org/get', (error, response, body) => {
  if (error) {
    console.error('Oh shoot. Something went wrong:');
    console.error(error.message);
    return;
  }
 
  console.log('Request done. Response status code: %d', response.statusCode);
});
```

Now run this code by executing the following command:


```js
node promises.js
```

As you can see, we pass in the callback function as a second argument to `request.get` call. The library will automatically execute this function when the response for the HTTP request comes in. It will pass three arguments. The first argument is the potential error or `null` if it was successful. The second argument is the HTTP response and the third argument is the response body.

If we use `fetch` instead of the `request.get` we can leverage Promises as fetch will return a `Promise` instead of accepting a callback as a second argument. A `Promise` is an object that has two important methods: `then()` and `catch()`. `then()` can receive 1 or 2 arguments and `catch()` can be used to handle errors.

For `then()`, the first function argument is called if the result of the call was successful. The second function will be called if there was an error resolving the Promise. We’ll look into the difference between that error handler and `catch()` later.

Replace the previous code with the following to start using Promises:

```js
fetch('http://httpbin.org/get')
.then(response => {
  console.log('Request using Promises done. Response status code: %d', response.status);
})
.catch(error => {
  console.error('Oh shoot. Something went wrong with the promise code:');
  console.error(error.message);
});
```

Re-run the code by executing again `node promises.js`.

So far there is no big difference from the callback code aside from it being a bit cleaner. The real magic comes when we want to do some data manipulation or make multiple calls. For this the general rule is that if the handler function that we pass to `then` or `catch` returns a value or another Promise, the Promise-chain will continue.

As an example add a function that extracts the status code and returns it:

```js
function extractStatusCode(response) {
  return response.status;
}
 
fetch('http://httpbin.org/get')
.then(extractStatusCode)
.then(statusCode => {
  console.log('Request using Promises, part II. Response status code: %s', statusCode);
})
.catch(error => {
  console.error('Oh shoot. Something went wrong with the promise code:');
  console.error(error.message);
});
```

Run the code again. The output in the console should be the same but our code is more structured.

This code will first perform the HTTP request, then call the `extractStatusCode` function and once that function returned it will execute our anonymous function that will log the response status code.

## Catching Errors

![Decorative: GIF of little girl failing to catch a ball](https://www.twilio.com/blog/wp-content/uploads/2016/09/YS8PLVpcyhjaXzBZwzJ93O93W5blxqXQg-qqTIFEBXG0EG-OLRlgERwItSphhZ9imcpJQ35f0I7-s2FAZcQUHktJjU3-iMOTYuK2eFTQcctukZoxW9TzIvNAtrTNnR5YVtd7S5tX-4.png)

Now that we are using Promises we might hit an issue. All of our code will fail silently if we don’t catch errors properly.

Imagine using Promises like wrapping your whole code into a try {} block. Your code will just silently fail unless you catch them explicitly. Catching errors is hugely important and not just â€˜common courtesy’.

In order to properly catch errors we have two options. The first way is to pass a second function into our then() call.

Make the following changes to your code to test this:

```js
function extractStatusCode(response) {
  return response.status;
}
 
fetch('invalid URL')
.then(extractStatusCode, errorInFetch => {
  console.error('An error occurred in the fetch call.');
  console.error(errorInFetch.message);
  // return null as response code since no request has been performed
  return null;
})
.then(statusCode => {
  console.log('Request using Promises. Response status code: %s', statusCode);
})
.catch(error => {
  console.error('This will never be executed');
});
```

When you run this code you’ll see that it will hit the error handler we added and print the respective messages to the screen:

![Screenshot of Terminal with error message](https://www.twilio.com/blog/wp-content/uploads/2016/09/MDMEk6tcP9UztGgMMeYhTJVy9vSDPNxF-TE5gkr5HHGeoFimdsz1v985Uu0kAVwqQvzdRE25IXavzCaSJKhJqi5iDV0OX0I6kipDWE8vvOMlwbnbMC_0f2sGdTIGTWN42qRWG4ek-4.png)

However it is not executing the `catch` handler because we are returning a value of `null` in the handler. From that point on the Promise chain is considered to be on the happy path again since the error has been handled.

We can make sure that it continues treating this as an error by `throw`ing the error or returning by returning a new Promise using `Promise.reject(error)`:

```js
function extractStatusCode(response) {
  return response.status;
}
 
fetch('invalid URL')
.then(extractStatusCode, errorInFetch => {
  console.error('An error occurred in the fetch call.');
  console.error(errorInFetch.message);
  // forward the error
  return Promise.reject(errorInFetch);
})
.then(statusCode => {
  console.log('Request using Promises. Response status code: %s', statusCode);
})
.catch(error => {
  console.error('This will now be executed as another exception handler.');
});
```

Now that we know how to handle an error with `then()` what’s the difference between this and `catch()`?

To understand this let’s fix our `fetch` snippet again to use a valid url and instead break the `extractStatusCode` function by overriding `response` with `undefined` before accessing the `status` property:

```js
function extractStatusCode(response) {
  response = undefined;
  return response.status;
}
 
fetch('http://httpbin.org/get')
.then(extractStatusCode, errorInFetch => {
  console.error('This will not be executed.');
  console.error(errorInFetch.message);
  // forward the error
  return Promise.reject(errorInFetch);
})
.then(statusCode => {
  console.log('Request using Promises. Response status code: %s', statusCode);
})
.catch(error => {
  console.error('There was an error somewhere in the chain.');
  console.error(error.message);
});
```

![](https://www.twilio.com/blog/wp-content/uploads/2016/09/6i33oKjzuVOypuMfZrOrLoDwwpQZNnqg1Bj6A96ggBi1f1jykx51RNVvHUoZity2m8ssF7dTdA0GHF8lkZVXnfJUiK0BPOnLwTT3ywctJhZpkJ_-BrYyOnDbUi_KJjy1OE4A8xOT-4.png)

The error handler in the `then()` part isn’t executed because this handler is only for the previous Promise and not the handler. However our `catch()` handler will be executed since it catches any errors that happen in the chain.

## Executing in Parallel

![Decorative GIF: hacking scene with two people coding on one keyboard](https://www.twilio.com/blog/wp-content/uploads/2016/09/gkYYQjOd3_hIFskG7pg6MBnP8uvvZFAL2j7xUHL-nPB7OvM835olCSo0594rCSnmvMl63Zyms_vZqHz1aKzPmY4wN6FpnQEhVYy-jWUw68YuPPrz6ieaW4VPxAXdYGWza_TdZBsc-4.png)

This is where the magic of Promises comes in. Consider the case in which we want to send multiple HTTP requests or do multiple asynchronous calls and want to know when they’re done.

The endpoints we want to request are held in an array. Using callbacks this can be quite a mess. To accomplish it we have to use counters in the callbacks to check if we are done and other similar hacks.

With Promises we can simply [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) over the array of messages, return the Promise in the map function and pass the resulting array into the built-in function `Promise.all()`. This will return a new Promise that resolves as soon as all calls succeed, or rejects once one of them fails.

```js
const queryParameters = ['ahoy', 'hello', 'hallo'];

const fetchPromises = queryParameters.map(queryParam => {
Â  return fetch(`http://httpbin.org/get?${queryParam}`)
Â  Â  .then(response => {
Â  Â  Â  // parse response body as JSON
Â  Â  Â  return response.json()
Â  Â  })
Â  Â  .then(response => {
Â  Â  Â  // extract the URL property from the response object
Â  Â  Â  let url = response.url;
Â  Â  Â  console.log('Response from: %s', url);
Â  Â  Â  return url;
Â  Â  });
});

Promise.all(fetchPromises).then(allUrls => {
Â  console.log('The return values of all requests are passed as an array:');
Â  console.log(allUrls);
}).catch(error => {
Â  console.error('A call failed:');
Â  console.error(error.message);
});
```

If you run this code you should multiple requests being made. However there is no guarantee in which order the calls are run and finished as they are executed in parallel.

## Executing in Series

![Decorative GIF: People standing in a queue bumping into each other](https://www.twilio.com/blog/wp-content/uploads/2016/09/5vTdWZEcbtgpgO-Wg4a7P_BPjeCG0DWjeK4uMsPlUvYgoAx0fASq-NJHwN9n_hjtkzly4zc2D0d3h-Az7PwrN1CiQm0DUCsII2j38mXtewOoYCKMaWGnNjsOPpIIGaiPWbhd4QRR-1.png)

While executing in parallel is cool and performant we sometimes have to make several calls in series due to restrictions or dependencies. We can also use Promises for this.

Chaining Promises when you know all necessary calls is super easy to do. However, it’s more complicated if we dynamically generate the asynchronous functions we need to execute.

There is a way we can get this done:

```js
const queryParameters = ['ahoy', 'hello', 'hallo'];

let mostRecentPromise = Promise.resolve([]); // start with an immediately resolving promise and an empty list
queryParameters.forEach(queryParam => {
Â  // chain the promise to the previous one
Â  mostRecentPromise = mostRecentPromise.then(requestedUrlsSoFar => {
Â  Â  return fetch(`http://httpbin.org/get?${queryParam}`)
Â  Â  Â  .then(response => {
Â  Â  Â  Â  // parse response body as JSON
Â  Â  Â  Â  return response.json()
Â  Â  Â  })
Â  Â  Â  .then(response => {
Â  Â  Â  Â  // extract the URL property from the response object
Â  Â  Â  Â  let url = response.url;
Â  Â  Â  Â  console.log('Response from: %s', url);
Â  Â  Â  Â  requestedUrlsSoFar.push(url);
Â  Â  Â  Â  return requestedUrlsSoFar;
Â  Â  Â  });
Â  });
});

mostRecentPromise.then(allUrls => {
Â  console.log('The return values of all requests are passed as an array:');
Â  console.log(allUrls);
}).catch(error => {
Â  console.error('A call failed:');
Â  console.error(error.message);
});
```

The concept here is to chain the calls and execute the next one once the previous one resolves by wrapping it into a `then()` handler. This is the same approach we would do manually if we knew the amount of calls.

Right now we are using a [`forEach`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) loop for this. This works but it isn’t really the most readable solution. To improve this we can use the [`reduce`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) method of our array.

Modify the code accordingly:

```js
const queryParameters = ['ahoy', 'hello', 'hallo'];

let mostRecentPromise = queryParameters.reduce((previousPromise, queryParam) => {
Â  return previousPromise.then(requestedUrlsSoFar => {
Â  Â  return fetch(`http://httpbin.org/get?${queryParam}`)
Â  Â  Â  .then(response => {
Â  Â  Â  Â  // parse response body as JSON
Â  Â  Â  Â  return response.json()
Â  Â  Â  })
Â  Â  Â  .then(response => {
Â  Â  Â  Â  // extract the URL property from the response object
Â  Â  Â  Â  let url = response.url;
Â  Â  Â  Â  console.log('Response from: %s', url);
Â  Â  Â  Â  requestedUrlsSoFar.push(url);
Â  Â  Â  Â  return requestedUrlsSoFar;
Â  Â  Â  });
Â  });
}, Promise.resolve([]));

mostRecentPromise.then(allUrls => {
Â  console.log('The return values of all requests are passed as an array:');
Â  console.log(allUrls);
}).catch(error => {
Â  console.error('A call failed:');
Â  console.error(error.message);
});
```

The overall approach here is the same as with the `forEach` loop. We specify a starting value of `Promise.resolve([])` and call the `reduce` method on the `messages` array with a function that receives two arguments. One is the previous return value and the other is the current value of the array that we are accessing. This way we can `reduce` the array to a single value. In our case this will be the most recent Promise that we can then use to know when everything is done.

## Turning Callback Code Into a Promise

Now that we know how to use Promises we have a problem to solve. What do we do with asynchronous code that doesn’t support Promises? For this we can wrap the function into a new function and use the `new Promise()` constructor. This constructor receives a function with two arguments:  `resolve` and `reject`. These arguments are functions we call when we want to resolve or reject a promise.

Here’s an example function that reads a file from disk and returns the content in a Promise:

```js
const fs = require('fs');

function readFileWithPromise(path) {
Â  return new Promise((resolve, reject) => {
Â  Â  fs.readFile(path, 'utf8', (err, content) => {
Â  Â  Â  if (err) {
Â  Â  Â  Â  return reject(err);
Â  Â  Â  }
Â  Â  Â  return resolve(content);
Â  Â  });
Â  });
}

readFileWithPromise('/etc/hosts').then(content => {
Â  console.log('File content:');
Â  console.log(content);
}).catch(err => {
Â  console.error('An error occurred reading this file.');
Â  console.error(err.message);
});
```

When we call `new Promise()` with a function as an argument, this function will immediately get executed asynchronously. We then execute `fs.readFile` with the necessary arguments. Once the callback of the `readFile` call is executed we check whether there is an error or not. If there is an error we will `reject` the Promise with the respective error. If there is no error we `resolve` the Promise.

Recently Node.js also introduced a new function called [`promisify`](https://nodejs.org/api/util.html#util_util_promisify_original) in the built-in `util` module. This function allows you to easily turn built-in callback-based functions into promise-based functions. [Check out the documentation for more information](https://nodejs.org/api/util.html#util_util_promisify_original).

## Conclusion

Now you hopefully have a better idea of Promises and are ready to ditch the times of the old _callback hell_ for some cleaner and more maintainable code. And if you are hooked you should check out what the future of JavaScript is going to bring with [async/await](https://www.twilio.com/blog/2015/10/asyncawait-the-hero-javascript-deserved.html) to further improve asynchronous programming in JavaScript.

Also make sure to let me know what your experience with Promises is and why you love it (or hate it). Maybe you even have a crazy hack using Promises you want to show off? Just drop me a line:

- Email: dkundel@twilio.com
- Twitter: [@dkundel](https://twitter.com/dkundel)
- GitHub: [dkundel](https://github.com/dkundel)

---

_[A quick guide to JavaScript Promises](https://www.twilio.com/blog/2016/10/guide-to-javascript-promises.html) was originally published on the [Twilio Blog](https://www.twilio.com/blog) on October 3, 2016._