Skip to main content

Promises in 4 minutes

· 4 min read
Luke Owen
Lead Front End Developer @ Lunio

Promises are one of the more exciting new features in ES6 (ES2015). They simplify asynchronous programming because they look as if they’re executing in a top down way (synchronously).

This avoids using a shed-load of callbacks, known as callback hell.

So what's a promise?

Simply put, a promise is a function that promises to return a value at some point in the future. They’re asynchronous so the rest of your code will still execute while waiting for the response.

Once you create a promise, it has 3 possible states:

  • Pending: the initial state, which just means a value hasn’t been returned yet
  • Fulfilled: the function completed successfully
  • Rejected: the function failed

When a promise is fulfilled or rejected, it is considered settled. At this point you can’t change it again, so you can’t fulfil a previously rejected promise.

You can attach a callback to the promise to handle the fulfilled response or the reason it was rejected.

Creating a promise

To create a promise use the new Promise constructor, this takes a function with two parameters (both functions):

  • resolve – called when the promise fulfils
  • reject – called when the promise fails
const promiseDemo = new Promise((resolve, reject) => {

// Resolve 50% of the time
if(Math.random() < 0.5) {
resolve('it worked');
}

// Since you cant change the outcome of the promise,
// if it resolves then this rejection wont run
reject('this didn\'t work');
});

console.log(promiseDemo);

Handling a resolve

Expanding this example, we can add a callback using then()

This is called if the promise resolves, and contains the value from the resolve as a parameter (assuming you passed one)

promiseDemo.then((success) => {
// Logs the resolved value to the console
// If the promise rejects: it won't run
console.log(success);
});

Handling a reject

Handling a rejection is pretty much the same, except we use catch()

If you don't catch a rejection: Chrome and Firefox will both get antsy and throw warning messages in your console, so it's a good idea to always handle them.

promiseDemo.catch((fail) => {
// Logs the rejected value to the console
console.log(fail);
});

Putting it together

This example uses a function to return a promise, I've also decided not to use arrow functions here.

Other than that its the same as the examples above.

// This function returns a promise
function demoPromise (data) {
return new Promise(function(resolve, reject) {

if(Math.random() < 0.5) {
resolve('it worked');
}

reject('this didn\'t work');
});
}

// Call the promise, then handle its success or failure
demoPromise('someData')
.then(function(success) {
console.log(success);
})
.catch(function(fail) {
console.log(fail);
});

Chaining

The then() and catch() methods themselves return promises, this means we can chain them.

Each subsequent call of then() will only be run after the previous one completes successfully.

This comes in really handy when you want to run a bunch of functions in order, for example when making AJAX calls.

demoPromise('someData')
.then((success1) =>{
// do a thing and return a new value
})
.then((success2) =>{
// do another thing
})
.then((success3) =>{
// do another thing
})
.catch((fail) => {
console.log(fail);
});

The catch() method at the bottom will catch errors from anywhere in the promise chain, so it provides a fallback in case any of the then() methods fail.

For this reason its good practice to always end a promise chain with a catch()

Conclusion

Promises are incredibly powerful new additions to ES6, and they form a pretty fundamental part of some other new ES6 features like the fetch standard for AJAX calls.

The popular Axios library (an alternative to fetch) is also promise based.

They also have really good support across the board, with the 'usual' exception of Internet Explorer.

CanIUse Promises