The Promise is the key feature of JavaScript asynchronous programming. Whether you love it or hate it, you have to understand it.
Here I’ve provided 10 code challenges about Promise, from basic to advanced. And you should guess the output of these code snippets.
Are you ready? Challengers!
Challenge 1: Promise Construtor
What is the output of this code snippet?
console.log('start');
const promise1 = new Promise((resolve, reject) => {
console.log(1)
})
console.log('end');
Analysis
The first challenge is very simple.
We know:
Synchronized code blocks are always executed sequentially from top to bottom.
When we call new Promise(callback), the callback function will be executed immediately.
Result
So this code is to sequentially output start
, 1
, end
.
Challenge 2: .then()
What is the output of this code snippet?
console.log('start');
const promise1 = new Promise((resolve, reject) => {
console.log(1)
resolve(2)
})
promise1.then(res => {
console.log(res)
})
console.log('end');
Analysis
In this code snippet, a piece of asynchronous code appears. That is, the callback function in .then().
Remember, the JavaScript engine always executes synchronous code first, then asynchronous code.
When encountering this problem, we only need to distinguish between synchronous code and asynchronous code.
Result
So the output is start , 1 , end and 2 .
Challenge 3: resolve()
What is the output of this code snippet?
console.log('start');
const promise1 = new Promise((resolve, reject) => {
console.log(1)
resolve(2)
console.log(3)
})
promise1.then(res => {
console.log(res)
})
console.log('end');
Analysis
This code is almost the same as the previous code; the only difference is that there is a console.log(3)
after resolve(2)
.
Remember, the resolve method does not interrupt the execution of the function. The code behind it will still continue to execute.
Result
So the output result is start
, 1
, 3
, end
and 2
.
Because I have met some guys who think resolve
will interrupt the execution of the function, I emphasize it here.
Challenge 4: resolve() isn’t called
What is the output of this code snippet?
console.log('start');
const promise1 = new Promise((resolve, reject) => {
console.log(1)
})
promise1.then(res => {
console.log(2)
})
console.log('end');
Analysis
In this code, the resolve method has never been called, so promise1 is always in the pending state. So promise1.then(…) has never been executed. 2 is not printed out in the console.
Result
So the output result is start
, 1
, end
.
Challenge 5: The One That’s There to Confuse You
What is the output of this code snippet?
console.log('start')
const fn = () => (new Promise((resolve, reject) => {
console.log(1);
resolve('success')
}))
console.log('middle')
fn().then(res => {
console.log(res)
})
console.log('end')
Analysis
This code deliberately adds a function to confuse challengers, and that is fn
.
But please remember that no matter how many layers of function calls there are, our basic principles remain the same:
Execute synchronous code first, then asynchronous code
Synchronous code is executed in the order in which it was called
Result
So the output result is start
, middle
, 1
, end
and success
.
Challenge 6: The One With a Fulfilling Promise
What is the output of this code snippet?
console.log('start')
Promise.resolve(1).then((res) => {
console.log(res)
})
Promise.resolve(2).then((res) => {
console.log(res)
})
console.log('end')
Analysis
Here Promise.resolve(1)
will return a Promise object whose state is fulfilled
and the result is 1
. It is synchronous code.
So the output result is start
, end
, 1
and 2
.
Ok, do you think these challenges are easy?
In fact, these are just appetizers. The difficulty of Promise is that it appears with setTimeout
. Next, our challenges will be even more difficult.
Are you ready? Challengers!
Challenge 7: setTimeout vs Promise
What is the output of this code snippet?
Analysis
Pay attention!
This is a very difficult question. If you can answer this question correctly and explain the reason, then your understanding of asynchronous programming in JavaScript has reached an intermediate level.
Before explaining this question, let us discuss the relevant theoretical basis.
We said before that the synchronous code is executed in the order of calling, so in what order are these asynchronous callback functions executed?
Some might say that whoever finishes first will execute first. Well, that’s true, but what if two async tasks complete at the same time?
For example, in the above code, the timer of setTimeout
is 0 second, and Promise.resolve()
will also return a fulfilled Promise object immediately after execution.
Both asynchronous tasks are completed immediately, so whose callback function will be executed first?
Some juniors may say that setTimeout
is in front, so print setTimeout
first, and then print resolve
. Actually, this statement is wrong.
We know that many things are NOT performed in a first-in, first-out order, such as traffic.
Priority
We generally divide vehicles into two categories:
General vehicles
Vehicles for emergency missions. Such as fire trucks and ambulances.
When passing through crowded intersections, we will allow fire trucks and ambulances to pass first. Emergency vehicles have more priorities than other vehicles. Keywords: priorities.
In JavaScript EventLoop, there is also the concept of priority.
Tasks with higher priority are called microtasks. Includes:
Promise
,ObjectObserver
,MutationObserver
,process.nextTick
,async/await
.Tasks with lower priority are called macrotasks. Includes:
setTimeout
,setInterval
andXHR
.
Although setTimeout
and Promise.resolve()
are completed at the same time, and even the code of setTimeout
is still ahead, but because of its low priority, the callback function belonging to it is executed later.
Result
So the output result is start
, end
, resolve
and setTimeout
.
Challenge 8: Microtasks mix Macrotasks
What is the output of this code snippet?
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
});
promise.then((res) => {
console.log(res);
});
console.log(4);
Analysis
This challenge is easy to complete if you already understand the previous code challenge.
We just need to do three steps:
Find the synchronization code.
Find the microtask code
Find the macrotask code
First, execute the synchronization code:
Output 1
, 2
and 4
.
Then execute microtask:
But here is a trap: Since the current promise is still in the pending state, the code in this will not be executed at present.
Then execute macrotask:
And the state of the promise
becoming to fulfilled
.
Then, with Event Loop, execute the microtask again:
Challenge 9: Prioritise Between Microtasks and Macrotasks
Before we introduce the priority between microtasks and macrotasks, here we look at the case of alternate execution of microtasks and macrotasks.
What is the output of this code snippet?
const timer1 = setTimeout(() => {
console.log('timer1');
const promise1 = Promise.resolve().then(() => {
console.log('promise1')
})
}, 0)
const timer2 = setTimeout(() => {
console.log('timer2')
}, 0)
Analysis
Some friends may think microtasks and macrotasks are executed like this:
Execute all microtasks first
Execute all macro tasks
Execute all microtasks again
cycle through
But the above statement is wrong.
The correct understanding is:
Execute all microtasks first
Execute one macrotask
Execute all (newly added) microtasks again
Execute next macrotask
Cycle through
Just like this:
Or like this:
So in the above code, the callback function of Promise.then
will be executed before the callback function of the second setTimeout
, because it is a microtask and has been cut in line.
Result
Challenge 10: A Typical Interview Question
Well, this is our final challenge. If you can correctly say the output of this code, then your understanding of Promise is already very strong. And the same type of interview questions won’t bother you in any way.
What is the output of this code snippet?
console.log('start');
const promise1 = Promise.resolve().then(() => {
console.log('promise1');
const timer2 = setTimeout(() => {
console.log('timer2')
}, 0)
});
const timer1 = setTimeout(() => {
console.log('timer1')
const promise2 = Promise.resolve().then(() => {
console.log('promise2')
})
}, 0)
console.log('end');
Analysis
This challenge is an upgraded version of the previous challenge, but the core principle remains the same.
Remember what we learned earlier:
Sync code
All microtasks
First macro task
All newly added microtasks
Next macro task
…
So:
Execute all sync code:
2. Execute all microtasks
3. Execute the first macro task
Note: In this step, the macrotask adds a new microtask to the task queue.
4. Execute all newly added microtasks
5. Execute the next macro task
Result
So the output is this.
Conclusion
For all similar questions, you just need to remember three rules:
The JavaScript engine always executes synchronous code first, then asynchronous code.
Microtasks have a higher priority than macrotasks.
3. Microtasks can cut in lines in Event Loop.
Thanks for reading.
I take this substack website as my digital home. If you want to build a closer connection with me, please consider subscribing to me.