1. Introduction
Suppose you ordered a burger in a restaurant. The cashier will take your order and will promise you that the burger will be delivered. After some time once the burger is available, your burger will be delivered. It is also possible that the burger will not be delivered due to some problem. The cashier will inform you about this with a regret message. This analogy can be used to understand the behavior of Promise
in JavaScript.
According to the MDN documentation:
The
Promise
object represents the eventual completion or failure of an asynchronous object and its resulting value.
Eventual here means, the asynchronous method instead of returning value immediately returns a promise to return a value some point of time in future.
When a promise is created, its value is not necessarily known. A Promise
is a proxy for the value returned by the asynchronous method some point in future.
2. Creating a Promise
The constructor syntax for a promise object is:
let promise = new Promise(function(resolve, reject) { // executor which produces some results or fails with error });
Following are important points to be noted:
- The function passed to the
Promise
is called the executor. When thePromise
is created, executor runs automatically. - The executor contains the code which produces a result eventually.
- The arguments
resolve
andreject
are callbacks provided by the JavaScript itself. resolve
andreject
expect only one argument (or none). Any additional arguments will be ignored.
Executor contains code which does the processing and returns a result some point in future. When executor obtains the result, it should call one of these callbacks:
- resolve(value): If the job (asynchronous method) was successfully executed, with the result
value
. - reject(error): If the job (asynchronous method) was unsuccessful due to an error, with the
error
object.
Let us understand this by writing some code. Following is an example of Promise
where job was successfully executed.
let promise = new Promise(function(resolve, reject) { // executor function which will run automatically // after 1 second call the callback 'resolve' with result "done" setTimeout(() => resolve("success"), 1000); });
Following is an example of Promise
where an error occurred:
let promise = new Promise(function(resolve, reject) { // executor function which will run automatically // after 1 second call the callback 'reject' with error object setTimeout(() => reject(new Error("Failure")), 1000); });
3. States of Promise
Promises have three possible mutually exclusive states:
- fulfilled: A promise is fulfilled if
promise.then(f)
will callf
“as soon as possible”. Simply, if promise is resolved then it is said to be fulfilled. - rejected: A promise is rejected if
promise.then(undefined, f)
will call f “as soon as possible.” Simply, if promise is rejected then it is said to be rejected. - pending: A promise is pending if it is neither fulfilled nor rejected.
4. Executor can call either reject or resolve
The exactor should call either resolve
or reject
. That is, a Promise
can result is either a result or an error. Any further calls to resolve
or reject
are ignored.
let myPromise = new Promise(function (resolve, reject) { setTimeout(() => { resolve("foo"); resolve("foo2"); //ignored resolve("foo3"); //ignored reject("error"); //ignored }, 1000); });
5. Immediately calling resolve/reject
Suppose there is case when the Promise
was created, there was no job remaining to be done. In this case you can immediately call resolve
or reject
.
let promise = new Promise(function(resolve, reject) { // immediately calling resolve resolve(17); // immediately call resolve with value 17. });
6. Consuming result/error of Promise – try, catch and finally (an introduction)
We’ll now discuss then
, catch
and finally
with respect to promises. We’ll discuss these in brief and will discuss in detail in separate tutorials.
6.1 then
Following is an example of using then
. In this case ‘success’ is the result.
let myPromise = new Promise(function (resolve, reject) { setTimeout(() => { resolve("success"); }, 1000); }); myPromise.then( function (value) { console.log(value); // 'success' will be printed }, function (error) { console.log(error); } );
Following is an example of then with reject
.
let myPromise = new Promise(function (resolve, reject) { setTimeout(() => { reject(new Error("Failure")); }, 1000); }); myPromise.then( function (value) { console.log(value); }, function (error) { console.log(error); // 'failure' will be printed } );
Above can be written using arrow function as well:
myPromise.then( value => console.log(value), error => console.log(error) );
6.2 catch
catch
is used to catch errors. If you are only interested in errors, you can use catch
.
let myPromise = new Promise(function (resolve, reject) { setTimeout(() => { reject(new Error("Failure")); }, 1000); }); myPromise.catch((err) => console.log(err));
Another way to handle only errors in using then
with first argument as null
.
6.3 finally
finally
always runs either Promise
is resolve
or reject
. finally
is a good place to write cleanup code. One such common use of finally
is to use it to stop showing loading indicators on page.
let myPromise = new Promise(function (resolve, reject) { setTimeout(() => { reject(new Error("Failure")); }, 1000); }).finally(() => { console.log("do your cleanup here"); }); myPromise.catch((err) => console.log(err)); Output -------- do your cleanup here Error: Failure
6.4 Difference between finally and then(resolve, reject)
You may think that finally
is similar to then(resolve, reject)
, because then
with both resolve
and reject
will always run and will handle. However, there are few differences.
finally
does have arguments whereasthen
has arguments.finally
passes through result to the next handler. For example, in this code,finally
runs and does not consume the result which isError
.Error
is still handled by thecatch
.
let myPromise = new Promise(function (resolve, reject) { setTimeout(() => { reject(new Error("Failure")); }, 1000); }).finally(() => { console.log("do your cleanup here"); }); myPromise.catch((err) => console.log(err));