Using Promises for Async Programming in JavaScript
By: John Au-Yeung
Tags: JavaScript, Promises, Programming, Technology, Software Development

Promises

To do this in JavaScript, we can use promises in our code. Promises are objects that represent a process that runs for an indeterminate amount of time and may result in success or failure. To create promises in JavaScript, we use a Promise object that’s a constructor for creating promises. The Promise constructor takes a function called the executor function that has the resolve and reject parameters. Both parameters are functions that we call to either fulfill the promise (which means end it successfully with some value) or to reject the promise (which means returning some error value and marking the promise as failed). The function’s return value is ignored. Therefore, promises can’t return anything other than promises.

For example, we can define a promise in JavaScript like in the following code:

const promise = new Promise((resolve, reject) => {

setTimeout(() => resolve('abc'), 1000);

});

The code above creates a promise that fulfills the promise with the value abc after one second. Because we’re running setTimeout inside the executor function to resolve the promise with the value abc in one second, it’s asynchronous code. We can’t return the value abc within the callback function of setTimeout , so we have to call resolve('abc') to get the resolved value back. We can access the resolved value of the fulfilled promise by using the then function. The then function takes a callback function that takes the resolved value of the fulfilled promise as a parameter. You can get the value there and do what you want with it. For example, we can do something like:

const promise = new Promise((resolve, reject) => {

setTimeout(() => resolve('abc'), 1000);

}); promise.then((val) => {

console.log(val);

})

When we run the code above, we should see abc logged. As we can see, the promise will supply the value when the promise is fulfilled with a call to the resolve function.

A promise has three states. It can be pending, which means that the promise has been fulfilled or rejected. It can be fulfilled, which means that the operation completed successfully. Or it can be rejected, which means that the promise operation failed.

A pending promise can either be fulfilled with a value or be rejected with some error. When a promise is fulfilled, then the corresponding resolve value is picked up by the then function and the callback function passed into the then function is called. If a promise is rejected, then we can optionally catch the error with the catch function, which also takes a callback function with the error. Both the then and catch functions return promises, so they can be chained together.

For example, we can write something like:

const promise = (num) => {

return new Promise((resolve, reject) => {

setTimeout(() => {

if (num === 1) {

resolve('resolved')

} else {

reject('rejected')

}

}, 1000);

});

} promise(1)

.then((val) => {

console.log(val);

})

.catch((error) => {

console.log(error);

}) promise(2)

.then((val) => {

console.log(val);

})

.catch((error) => {

console.log(error);

})

In the code above, we have a function promise that returns a JavaScript promise that fulfills the promise with the value resolved when num is 1, and reject the promise with error rejected otherwise. So we run:

promise(1)

.then((val) => {

console.log(val);

})

.catch((error) => {

console.log(error);

})

Then the then function runs, the promise returned by the promise(1) function call is fulfilled since num is 1, and the resolved value is available set in val . So when we run console.log(val) we get resolved logged. When we run the code below:

promise(2)

.then((val) => {

console.log(val);

})

.catch((error) => {

console.log(error);

})

Then the catch function runs because the promise returned by the promise(2) function call is rejected, and the rejected error value is available set in error. So when we run console.log(error) we get rejected logged.

A JavaScript promise object has the following properties: length and prototype . The length always has its value set to 1 for the number of constructor arguments, which is always one. The prototype property represents the prototype of the promise object.

A promise also has the finally method to run code that’s run no matter whether the promise is fulfilled or rejected. The finally method takes a callback function as an argument, where any code that you want to run, regardless of the outcome of the promise, is run. For example, if we run:

Promise.reject('error')

.then((value) => {

console.log(value);

})

.catch((error) => {

console.log(error);

})

.finally(() => {

console.log('finally runs');

})

Then we get error and finally runs logged because the original promise was rejected with the reason error . Then whatever code in the callback of the finally method runs.

The main benefit of using promises for writing asynchronous code is that we can use them to run asynchronous code sequentially. To do this, we can chain promises with the then function. The then function takes a callback function that runs when the promise is fulfilled. It also takes a second argument when the promise is rejected. To chain promises, we have to return another promise as the return value of the first callback function of the then function. It can return other values, like nothing, if we don’t want to chain another promise to the existing promise. We can return a value, which will be resolved and the value retrievable in the next then function. It can also throw an error. Then the promise returned by then gets rejected with the thrown error as the value. It can also return an already fulfilled or rejected promise, which will get the fulfilled value when a then function is chained after it or get an error reason in the callback of the catch function.

For example, we can write the following:

Promise.resolve(1)

.then(val => {

console.log(val);

return Promise.resolve(2)

})

.then(val => {

console.log(val);

}) Promise.resolve(1)

.then(val => {

console.log(val);

return Promise.reject('error')

})

.then(val => {

console.log(val);

})

.catch(error => console.log(error)); Promise.resolve(1)

.then(val => {

console.log(val);

throw new Error('error');

})

.then(val => {

console.log(val);

})

.catch(error => console.log(error));

In the first example, we chained the promises and they all resolve to a value. All the promises were already resolved into values. In the second and last examples, we reject the second promise or throw an error. They both do the same thing. The second promise is rejected, and the error reason will be logged in the callback of the catch function. We can also chain pending promises, like in the following code:

const promise1 = new Promise((resolve, reject) => {

setTimeout(() => resolve(1), 1000);

}); const promise2 = new Promise((resolve, reject) => {

setTimeout(() => resolve(2), 1000);

}); promise1

.then(val => {

console.log(val);

return promise2;

})

.then(val => {

console.log(val);

})

.catch(error => console.log(error));