
Just imagine this. You wake up and get ready for work on a Monday morning. When you open your email there are a bunch of emails which you need to act on. First you think let’s have some breakfast on the way. You stop by at a self-service restaurant. You give an order for a plate of Dosa. It takes some time for the dosa to get prepared. You can only do one thing at a time. (Multitasking in humans is a misconception. We will not be able to do both the tasks properly if we try to do two things at once. Unless one of them is automatic action like walking on a treadmill and listening to a podcast for example.) You can either stand there and wait for the dosa, or you can sit there on the table and check your emails and start taking actions on them. You decide to do the latter. As soon as the dosa is ready, they call out the token number. You close the laptop and get your dosa and have it.
JavaScript is also a single threaded language, meaning it cannot do multiple things at the same time. However, it can hand off some long running jobs to another process and continue with its tasks and be notified when it is done and perform those tasks which need to be done after the long running job is complete (Eating the dosa in our earlier example). These long-running jobs can be a network call, SQL operation etc. What are Promises in JavaScript? Let’s see that with an example below:
const myPromise = new Promise((resolve, reject) => {
// call resolve when done
// here I am calling resolve after 3 seconds to simulate
setTimeout(() => resolve("Dosa is ready..."), 3000);
// call reject when there is an issue
//reject(new Error("Sorry, we are not able to make the Dosa now.."))
});
function doThisOnceResolved(result){
console.log(result);
}
myPromise
.then(doThisOnceResolved)
.catch(err => console.log(err.message));
console.log("Doing my Task 1");
console.log("Doing my Task 2");
console.log("Doing my Task 3");
Let’s breakdown this code.
The First part is setting up this promise – I am creating a new Promise object and passing it a function. It is called the resolver function. This function has 2 functions as parameters resolve and reject. We can call it whatever for example we can call it res and rej. It is only for our understanding. Within this function we do our long running job for example we do a network call or a SQL query or any operation which we want. This operation can succeed or fail. If it succeeds we call the resolve() in our case and if it fails we call the reject() method. Here we have simulated the long running job with a setTimeout function. This will invoke our function after the delay we have specified in milliseconds. In this case it is 3 seconds. We immediately get a reference to the promise object in myPromise.

The second part is how do we know when our task is done. We have our myPromise but it is in the pending state. This promise has a then() function. Whatever function we pass to this then() function is what will be executed if the promise gets resolved. In our case we have doThisOnceResolved function. Also, the data which was used when resolving the promise is passed to this function. “Dosa is ready…” in our case so we can log it. In case there was a failure, it invokes the method passed to the catch() function and logs the error message.
Now that we have defined what should happen once the long running job is done, let’s see how the JavaScript executes this code in real time. It runs the first part of creating the promise and gets the reference to the promise in myPromise variable. Next is a function declaration doThisOnceResolved() so nothing is executed here. Next we have set up the then and catch so JS knows what to do when operation is completed or an error occurs. But JS does not wait for this. It will continue execute the next lines of code. Here in our case we have 3 lines Doing my Task 1, 2, 3. We could have a lot more lines of code which could be executed here. Once 3 seconds is done in our case, the then() will be executed and we can see the output “Dosa is ready…” if it was successful.
One thing to note here is that when the long running job is completed, and the JS is still executing some lines of code, it will not stop immediately and execute the then or catch block. Only after it finishes all the lines of code it will be executed. Just like when we are working on our emails and responding to some important email if the dosa is ready, we will not just close our laptop or worse hit send and pick up our dosa 😊.
Now that we know the basics of working with Promises, let’s take it to the next level where we have a bunch of long running jobs. We may want to execute something when all of them succeed, any one of them succeed or maybe get the status of each regardless of they succeeded or failed.
Again, lets look at a real life example where we are writing the 12th board exams and we have different subjects for which we have to write the exams. In our example, let’s just take Physics, Mathematics and Chemistry. The board will not say that we have passed the 12th std even if we fail in any one subject. In JavaScript we can write the following code:
const physics = Promise.resolve("Physics Pass");
const maths = Promise.resolve("Maths Pass");
const chem = Promise.resolve("Chem Pass");
//const chem = Promise.reject(new Error("Chem Fail"));
const hallTicket = Promise.all([physics, maths, chem]);
hallTicket
.then((result) => {
console.log(result);
})
.catch(err => console.log(err.message));

In this case since we are resolving all the 3 subjects we get a result [ ‘Physics Pass’, ‘Maths Pass’, ‘Chem Pass’ ]. In case we reject even one subject e.g. Chem in the commented code above, we get the error message Chem Fail
Now let’s say we want to generate our markscard. In case, we want to know the status of each subject regardless of whether it is pass or fail.

For this, we can use Promise. allSettled as follows:
const physics = Promise.resolve("Physics Pass 80%");
const maths = Promise.resolve("Maths Pass 90%");
// const chem = Promise.resolve("Chem Pass");
const chem = Promise.reject(new Error("Chem Fail 20%"));
const hallTicket = Promise.allSettled([physics, maths, chem]);
hallTicket
.then((results) => {
results.forEach(r => {
if(r.status === 'fulfilled'){
console.log(`✅ ${r.value}`);
} else {
console.log(`❌ ${r.reason.message}`);
}
})
})
.catch(err => console.log(err.message) );
Now, lets see one developer real life problem. You are struck with a code issue. You send a message on Slack, Teams, Discord to a bunch of your friends. You get a lot of responses. You try a first few of them. One of the solutions works really works well. You are sorted. Of course in this case you have to thank each one of them but any one valid solution is good to move ahead.

For this kind of scenario you can use Promise.any. Let’s see it in an example:
const sol1 = Promise.resolve("Solution 1");
const sol2 = Promise.resolve("Solution 2");
const sol3 = Promise.reject(new Error("Solution 3"));
const final = Promise.any([sol1, sol2, sol3]);
final
.then((result) => {
console.log(result);
})
.catch(err => console.log(err.message) );
Here Solution 1 and 2 are valid. Even though the Solution 3 is not working, we get the first working solution in our result which is Solution 1 in this case. We ignore the other successful calls also, we just use the first successful result and move ahead.
Now, lets say you are trying to book a taxi. There are multiple vendors, you gather 2 of your friends and ask one of them to try booking in Uber, other one in Ola and you try in Namma Yatri. Once one of the booking is confirmed you don’t want the other 2 vendors. You want to immediately cancel the booking process on the other 2.

For this kind of situation in JavaScript we have Promise.race . Whichever completes first will be returned.
const sol1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("Solution 1 is ready..."), 3000);
});
const sol2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("Solution 2 is ready..."), 2000);
});
const sol3 = new Promise((resolve, reject) => {
setTimeout(() => resolve("Solution 3 is ready..."), 1000);
});
const final = Promise.race([sol1, sol2, sol3]);
final
.then((result) => {
console.log(result);
})
.catch(err => console.log(err.message) );
In this case we get an output of Solution 3 is ready… because it is completing in just 1 second. Here it is not cancelling the other 2 processes. we just take the result of the first successful operation and proceed.
