Take a look at the below code:
const promise = new Promise((resolve, reject) => {
resolve();
});
promise
.then(() => {
console.log('first');
})
.then(() => {
console.log('second');
})
.then(() => {
console.log('third');
})
.catch((error) => {
console.log('error', error);
});
If you execute the above code, you'll see the following output:
As you know each .then
handler will be executed one after the another and the value returned from the previous .then
handler is passed to the next .then
handler so we get the output of the above code as:
first
second
third
which is as expected.
But now, take a look at the below code:
const promise = new Promise((resolve, reject) => {
resolve();
});
promise
.then(() => {
console.log('first');
})
.then(() => {
setTimeout(() => {
console.log('second');
}, 2000);
})
.then(() => {
setTimeout(() => {
console.log('third');
}, 1000);
});
If you execute the above code you will see the following output:
We're getting the output as:
first
third
second
In the above code, the second .then
handler has a setTimeout function call with 2000 milliseconds(2 seconds) as the timeout so it will not be executed immediately, but the third .then
will be executed before the second .then
handler as it has a 1000 millisecond(1 second) timeout.
Therefore, we're seeing first
printed immediately and after 1 second, third
will be printed and after 2 seconds second
will be printed.
So If any .then
handler is making an API call or doing some long-running operation then the next .then
handler function might be executed immediately even If the long-running operation in the previous .then
handler is not completed yet.
When you attach multiple
.then
handlers, it’s not guaranteed that the next.then
handler will be called only after the previous.then
handler finishes if there is additional asynchronous code inside the handler.
There is a way to fix this default behavior.
We can return a new promise from the second .then
handler and only call resolve
or reject
once we are done with our API call or any other long-running operation.
This will guarantee that the next .then
will always be executed after the previous one has finished.
This is because when we call a promise, the Promise will wait until we explicitly call resolve
or reject
inside the promise.
The following is the code that will maintain the order sequence:
const promise = new Promise((resolve, reject) => {
resolve();
});
promise
.then(() => {
console.log('first');
})
.then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('second');
resolve();
}, 2000);
});
})
.then(() => {
setTimeout(() => {
console.log('third');
}, 1000);
});
As you can see, we're returning a promise from the second .then
handler so the next .then
handler will be executed only if the second .then
handler resolves.
If you execute the above code you will see the following output:
As you can see now, each .then
handle is executed only after the previous one is finished executed.
That's it about this article.
Thanks for reading!
Check out my recently published Mastering Redux course.
In this course, you will learn:
- Basic and advanced Redux
- How to manage the complex state of array and objects
- How to use multiple reducers to manage complex redux state
- How to debug Redux application
- How to use Redux in React using react-redux library to make your app reactive.
- How to use redux-thunk library to handle async API calls and much more
and then finally we'll build a complete food ordering app from scratch with stripe integration for accepting payments and deploy it to the production.
Want to stay up to date with regular content regarding JavaScript, React, Node.js? Follow me on LinkedIn.