Promise in Javascript Explained

Core Concepts with Examples from MDN

Hyunbin
3 min readFeb 5, 2021

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).

Pager or Buzzer used in restaurants and cafe, Source

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
If Fullfilled or Rejected, promise is Settled, Source

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 promises 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 promises 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.

--

--

Hyunbin
Hyunbin

Written by Hyunbin

Node.js and web developer using TypeScript. Undergraduate in Seoul National University.

No responses yet