Promise in Javascript Explained
What is Promise?
promise
is used for asynchronous operation.
This includes async
functions. In fact,await
inside an async function means that the following function is expected to return a promise
.
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}async function asyncCall() {
console.log('calling');
const result = await resolveAfter2Seconds();
console.log(result);
// expected output: "resolved"
}asyncCall();// Source: async function, MDN Web Docs
What is Asynchronous?
Queuing operations to be completed later.
Javascript is single threaded. Therefore, it must (1) execute code in order and must (2) finish executing previous code before moving onto the next.
In other words, Javascript has a single stack
on which the codes run.
This means that if there is a code that takes long time to execute, it blocks further codes from executing, therefore causing a performance issue.
promise
helps overcome this by putting such blocking task in a queue
and executing it after the stack
is empty.
const promiseA = new Promise( (resolutionFunc,rejectionFunc) => {
resolutionFunc(777);
});
// At this point, "promiseA" is already settled.
promiseA.then( (val) => console.log("asynchronous logging has val:",val) );
console.log("immediate logging");// produces output in this order:
// immediate logging
// asynchronous logging has val: 777// Note how 'immediate logging' is logged first.// Source: Promise, MDN Web Docs
For better understanding of call stack
and callback queue
check the following video by Philip Roberts.
States of Promises.
pending
, fulfilled
, rejected
promise
is similar to a pager — a metaphor used by Tyler McGinnis. promise
is either waiting to signal(=pending) or has signaled(=settled).
Difference compared to a pager is that in a promise
there are two types of signals that can be sent — success(=fulfilled) and failure(=rejected).
const promiseA = new Promise( (resolutionFunc,rejectionFunc) => {
resolutionFunc(777);
});// If the first argument(resolutionFunc) is invoked.
// The promise is settled as fulfilled (not rejected).
// The fulfilled value(777) is passed for further usage.const promiseB = new Promise( (resolve,reject) => {
reject(new Error());
});// Arguments' names are not important. Their positions matter.
After Promise Settles
Callback functions for fulfillment and rejection
When a promise
succeeds the success value is, when it fails the reason(error) is returned. For each type, a callback function should be provided.
For example, when a weather of a location is fetched, the promise
can either return the weather result(=success) or error(e.g. API key is not valid).
This is where then()
and catch()
is used. Note that both of them return promises, which allows the following chaining to be possible.
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA,handleRejectedA)
.then(handleFulfilledB,handleRejectedB)
.then(handleFulfilledC,handleRejectedC);// Source: Promise, MDN Web Docs
On Fufillment and Rejection
then()
can have up to two callback functions
It is common to see examples similar to the following code block. After a new Promise()
, there are multiple .then()
and one .catch()
in the end.
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA)
.then(handleFulfilledB)
.then(handleFulfilledC)
.catch(handleRejectedAny);// Source: Promise, MDN Web Docs
However, then()
can have up to two callback functions. In fact, this is more natural, since then()
returns a promise
which can be fulfilled or rejected.
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA,handleRejectedA)
.then(handleFulfilledB,handleRejectedB)
.then(handleFulfilledC,handleRejectedC);// Source: Promise, MDN Web Docs
The above code block can be rewritten as follows. If one of the promise
s get rejected, it finds the closest handleRejected
callback function.
const myPromise =
(new Promise(myExecutorFunc))
.then(handleFulfilledA)
.catch(handleRejectedA)
.then(handleFulfilledB)
.catch(handleRejectedB)
.then(handleFulfilledC)
.catch(handleRejectedC)
This is why in the following example, the last catch()
’s callback function is a handleRejectedAny
. It runs if any of the above four promise
s is rejected.
const myPromise =
(new Promise(myExecutorFunc)) // returns Promise
.then(handleFulfilledA) // returns Promise
.then(handleFulfilledB) // returns Promise
.then(handleFulfilledC) // returns Promise
.catch(handleRejectedAny);// Source: Promise, MDN Web Docs
Why Async and Await?
This article does not discuss the benefits of async functions, callbacks, etc. For better understanding, check the following video by Tyler McGinnis.