Javascript promise: resolve and reject
In this article, we will go through the concept of javascript promise. After reading this article, you will be able to
1. Understand what is a promise and why it is created.
2. How to create a promise.
3. Resolve or reject a promise.
4. Handle error in promise.
5. Chaining multiple promises.
What is a promise
A promise allows you to work with asynchronous operations or tasks. Asynchronous means an operation which executes without blocking other operations.
Example, consider a facebook page. When you open it, some portions of the page display first while the posts load later.
Here, loading of posts is performed as an asynchronous operation.
A promise allows you to perform a time taking task and process the result of the task as and when it(the result) becomes available without blocking the other operations.
Promise was introduced in ES6.
How to create promise?
A promise is created using constructor of Promise
class.
This constructor expects a function with two arguments. as shown below.
// create a promise const promise = new Promise(function(resolve, reject) { });
Inside the function body, the task which the promise is supposed to do is written. This is usually a task that takes time to complete or the task which you want to perform asynchronously.
If we take the facebook example, the task of fetching posts would be written inside the function body.
When creating a promise, a function is supplied in the
Promise
constructor. This function has two arguments : resolve
and reject
but they can be given any name of your choice.
These arguments are themselves functions and are used to signify whether the task written in promise executed successfully or failed.
If the task executed successfully, then you call resolve
function and pass it a value, if required.
Similarly, if the task failed due to some reason, then reject
function is called as shown below.
// create a promise const promise = new Promise(function(resolve, reject) { // asynchronous task if(successful) { resolve('Success!'); } else { reject('Error!'); } });
Promise constructor creates and returns a new promise object.
Remember that the task that the promise is supposed to execute should be written inside the promise constructor and it starts as soon as the promise is created.
Promise states
A promise has different states during its lifecyle. It can be in any of the following states according to the task it performs.
1. Pending
When the promise is created and while it is executing its task, it remains in the “pending” state.
If you do not call resolve
or reject
functions, the promise will be in “pending” state forever even if its task completes successfully.
2. Fulfilled
A promise enters “fulfilled” state after you call resolve
function. This indirectly means that when its task completes successfully since then only you would like to call resolve
.
But, even if you call resolve in case of an error, the state of promise will be “fulfilled”.
2. Rejected
State of the promise becomes “rejected” when reject
function is called which means that the task of promise failed due to some reason.
A promise executes a task asynchronously and when the task completes, you need some way to handle the result.
Coming back to facebook example, suppose a promise is fetching posts. When the post data is fetched, there should be a mechanism to display the post data on web page.
This is done using then()
method of a promise. then()
method is automatically invoked when you call resolve()
or reject()
from a promise.
Thus, then()
is a callback function.
then()
method takes two functions as arguments: One for handling the resolve()
callback and other for handling reject()
callback.
If you provide only one function argument, then it will be considered as a handler for resolve()
callback. Thus, then()
takes at least one function as argument.
These functions may or may not accept a value. If they accept a value, then it is populated with the value supplied by the resolve()
and reject()
functions.
Working of then()
will be better understood with the below illustration.
Promise example
Below is an example of how a promise is created and used to perform asynchronous task processing.
const p = new Promise(function(resolve, reject) { // time taking asynchronous task setTimeout(function() { // array of posts const posts = [{"postid": 1, "description": "Post One"}, {"postid": 1, "description": "Post One"}]; // resolve promise resolve(posts); },2000) }); // other tasks p.then(function(value) { console.log(value); });
Above example creates a promise with a function as an argument. For simulating a time taking task, setTimeout()
function is used.
Suppose the task written in promise returns an array of objects. When the task completes, resolve()
is called with the array as an argument.
After the promise definition, then()
method is called on it. then()
accepts a function as an argument which is the callback function for resolve()
as explained in the last section.
As soon as resolve()
is called, the function written inside then() is invoked. The argument to this function is populated with the value supplied by resolve()
function.
Here is the output of the above code.
Output shows an array of post objects that was returned by the resolve()
method.
When the promise is executing, other tasks can also execute simultaneously. Thus, a promise performs a task asynchronously.
There may be a possibility that the task performed by a promise fails or results in an error. If there is an error, then you need to call
reject()
from inside the promise.After calling
reject()
, we should also handle the error.There are following two ways to handle the error condition after calling
reject()
, any one of these may be used.
1. Error handler function in then()
If there is an error during task processing in a promise, then call reject()
function from inside the promise body.
As stated earlier, then()
accepts two functions as arguments where the second one is a callback function which is automatically called when reject()
is called from promise.
Value passed as argument to reject()
becomes available as an argument to the second argument of then()
. Example,
const p = new Promise(function(resolve, reject) { // time taking asynchronous task setTimeout(function() { // error condition reject('Oops! An error occurred'); },2000) }); // other tasks p.then(function(value) { console.log(value); }, function(error){ console.log(value); });
Above example prints
Oops! An error occurred
Remember that only the second argument function to then()
will be considered as error handler function.
2. Using catch()
Similar to then()
, promise provides a catch()
method, which acts as a handler for error raising inside promise.
catch()
, as the name sounds is used to catch error.
It accepts a function as argument. It is inside this function that you write the error handling mechanism.
Argument to this function receives the value sent by reject()
. Example,
const p = new Promise(function(resolve, reject) { // time taking asynchronous task setTimeout(function() { // error condition reject('Oops! An error occurred'); },2000) }); // other tasks p.then(function(value) { console.log(value); }); // error handling using catch p.catch(function(e){ console.log(e); });
Promise chaining
Promises can be chained meaning you can call
then()
multiple times one after another without creating a new promise.This is because
then()
returns a promise and you can always call then()
on a promise. Value returned by the first then()
is implicitly passed to the second then()
and so on. Example,
function increment(seconds) { // create promise const p = new Promise(function(resolve, reject) { // increment argument value seconds++; // resolve promise resolve(seconds); }); return p; } // promise chaining increment(0).then(increment).then(function(v){console.log(v);})
Above example demonstrates promise chaining and it can be broken into multi-lines to understand how it works
1. increment(0).
2. then(increment).
3. then(function(v) { console.log(‘Final result is ‘ + v); });
1. increment()
function is invoked with 0 as argument. It creates a promise and returns it. The promise increases the value of function argument by 1 and calls resolve()
.
2. Since increment()
returns a promise, we can call then()
over it. Now, then()
expects a function as argument and we are passing it increment()
which is a function.
then()
returns a value by calling resolve()
and this returned value is implicitly passed to increment()
.
3. Step 2 calls increment()
which again performs Step 1 and returns a promise. then()
is called on this promise and prints the value returned by the promise.
Output of this example will be
Final result is 2
Promise.all()
all()
method accepts an array of multiple promises and waits till all promises call resolve()
or any one promise calls reject()
. Example,
// create a promise const p1 = new Promise(function(resolve, reject) { setTimeout(function() { resolve('Promise 1'); }, 2000) }); // create another promise const p2 = new Promise(function(resolve, reject) { setTimeout(function() { resolve('Promise 2'); }, 1000) }); // invoke all with both the promises Promise.all([p1,p2]) .then(function(v){ console.log(v); });
Above code waits for 2 seconds and then prints
[ “Promise 1”, “Promise 2”]
If any one of the promise calls reject()
or results in an error, then all()
returns an error. In that case, it does not wait for other promises to finish.
Example,
// create a promise const p1 = new Promise(function(resolve, reject) { setTimeout(function() { resolve('Promise 1'); }, 2000) }); // create another promise const p2 = new Promise(function(resolve, reject) { setTimeout(function() { reject('Error!'); }, 1000) }); // invoke all with both the promises Promise.all([p1,p2]) .then(function(v){ console.log(v); }) .catch(function(e){ console.log(e); });
Above code will wait for 1 second and then print
Error!
Note that in this example, we have used catch()
method to handle reject()
. But you may also add a second argument function to then()
.
Promise.race()
race() also accepts an array of promises but it returns the value from the promise that calls resolve()
or reject()
first. Thus, the result will be the value of promise which resolves or rejects before all other promises. Example,
// create a promise const p1 = new Promise(function(resolve, reject) { setTimeout(function() { resolve('Promise 1'); }, 2000) }); // create another promise const p2 = new Promise(function(resolve, reject) { setTimeout(function() { resolve('Promise 2'); }, 1000) }); // invoke all with both the promises Promise.race([p1,p2]) .then(function(v){ console.log(v); }) .catch(function(e){ console.log(e); });
Above code will wait for 1 second and then print
Promise 2
since the second promise will resolve first.
If there are multiple promises that call resolve()
or reject()
at the same time, then the first one in the argument array to race()
will win.
Why create promise
Now when you have learnt about promise syntax and chaining, you are ready to understand the benefit you get from it.
A promise is used to perform asynchronous operation. You might say that the same can be achieved by a callback function.
But when there are multiple callback functions that are dependent on each other or nested inside each other, then it becomes hard to maintain them.
Consider the code snippet below.
setTimeout( function(v1){ setTimeout(function(v2) { setTimeout(function(v3) { setTimeout(function(v4) { }, 1000); }, 1000); }, 1000); }, 1000);
This code is often referred to as Callback hell since it is very difficult to manage.
Promise solves this problem by chaining and you don’t need to nest more than one level. Thus, below code is very easy to maintain.
taskOne() .then(function(){ return taskTwo(); }) .then(function() { return taskThree(); }) .then(function() { return taskFour(); });
Arrow functions in promise
Till now we have seen functions at multiple places while using promises. In place of these functions, you could also use javascript arrow functions.
Arrow functions were also added in ES6 and they enable you to write functions in a shorter syntax. If you are not familiar with them, refer this article.
Using arrow functions, promise example can be modified as below.
const p = new Promise((resolve, reject) => { // time taking asynchronous task setTimeout(() => { // array of posts const posts = [{"postid": 1, "description": "Post One"}, {"postid": 1, "description": "Post One"}]; // resolve promise resolve(posts); },2000) }); // other tasks p.then((value) => { console.log(value); }, (error) => { console.log(error); });
That is all on promise in javascript ES6. Click the clap if you liked it.