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
Promiseobject 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
Promiseis called the executor. When thePromiseis created, executor runs automatically. - The executor contains the code which produces a result eventually.
- The arguments
resolveandrejectare callbacks provided by the JavaScript itself. resolveandrejectexpect 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
errorobject.
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.
finallydoes have arguments whereasthenhas arguments.finallypasses through result to the next handler. For example, in this code,finallyruns and does not consume the result which isError.Erroris 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));
