The eventloop mechanism is key to Javascript's implementation of pseudo-"multithreading." This part is also a question that is often asked in interviews, take a note for reference.
The execution model of JavaScript
To be clear, JavaScript is a single-threaded language. All tasks in JavaScript are divided into one of three categories:
- Synchronization tasks
- Asynchronous macro tasks
- Asynchronous microtasks
JavaScript implements asynchronous tasks by frequently switching between these tasks.

In terms of the overall execution mechanism, JavaScript handles the task at hand in the following order:
- If it is a synchronous task, it is executed directly in the main thread.
- If it is an asynchronous task, register the function in the Event Table, and when the asynchronous event completes (I/O, timeout, etc.), move the one-step callback function into the Event Queue.
- When the main thread task finishes executing, start executing the asynchronous callback task in the Event Queue (the same EventLoop is always the first microtask and then the macro task) until the execution is completed.
- This cycle is called Event Loop.
Macro tasks? Microtasks?
How to distinguish
- Macro tasks
setTimeout
,setInterval
,setImmediate
- Microtasks
Native Promise
process.nextTick
(node.js)Object.observe
MutationObserver
Enforcement mechanisms
Asynchronous events in an Event Queue are executed according to the following flowchart:

- First take a macro task from the macro task queue in the Event Queue and execute it.
- After each macro task is executed, check the microtask queue and execute all microtasks.
Some examples
Easy difficulty
console.log(1)
new Promise((resolve, reject) => {
console.log(2)
resolve()
}).then(res => {
console.log(3)
})
console.log(4)
Output: 1 2 4 3
Interpretation:
- First, execute the synchronization task
console.log(1)
and output1
. - When
a new Promise
is encountered, immediately executeconsole.log(2)
output2
, and registerthe .then
callback function in the Event Queue. - Continue with the synchronization task
console.log(4)
and output4
. - After the synchronization task is executed, execute the microtask
console.log(3)
and output3
.
Medium difficulty
console.log('start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
new Promise((resolve) => {
console.log('promise')
resolve()
}).then(() => {
console.log('then1')
}).then(() => {
console.log('then2')
})
console.log('end')
Output: start promise end then1 then2 setTimeout
Interpretation:
- First, execute the synchronization task
console.log (start)
and outputstart
. - When
setTimeout
is encountered, put the callback into the macro task queue. - When
a new
promise is encountered, immediately execute theconsole.log (promise)
output the promise, and put two .then callback functions intothe
microtask queue.
- Continue with the synchronization task
console.log(end)
and outputend
. - After the synchronization task is executed, execute the microtask console.log(then1, then2), and output
then1, then2
. - Finally, execute the macro task
console.log (
setTimeout) and outputsetTimeout
.
Complex difficulty
console.log('start')
setTimeout(() => {
console.log('setTimeout')
new Promise((resolve) => {
console.log('promise2')
resolve()
}).then( () => {
console.log('then3')
})
}, 0)
new Promise((resolve) => {
console.log('promise1')
resolve()
}).then(() => {
console.log('then1')
}).then(() => {
console.log('then2')
})
console.log('end')
Output: start promise1 end then1 then2 setTimeout promise2 then3
Interpretation:
- First, execute the synchronization task
console.log (start)
and outputstart
. - When
setTimeout
is encountered, put the callback into the macro task queue. - When
a new promise
is encountered, immediately executeconsole.log(
promise1) to outputpromise1
, and put two .then callback functions into the microtask queue.
- Continue with the synchronization task
console.log(end)
and outputend
. - After the synchronization task is executed, execute the microtask console.log(then1, then2), and output
then1, then2
. - Execute the macro task
console.log (
setTimeout) and outputsetTimeout
. - Execute
the new
promise from the previous macro task, immediately execute theconsole.log (
'promise 2'), outputpromise 2
, and put .then into the microtask queue.
- Execute the microtask
console.log (
then3) and outputthen3
.
Hell difficulty
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
Output: 1 7 6 8 2 4 3 5 9 11 10 12
Interpretation:
- First, execute the synchronization task
console.log(1)
and output1
. - When
setTimeout
is encountered, put the callback into the macro task queue. - When you encounter
process.nextTick
, put the function in the microtask queue. - When a
new Promise
is encountered, immediately executeconsole.log(7) output 7,
and putthe .then
callback function into the microtask queue. - When
setTimeout
is encountered, put the callback into the macro task queue. - After the synchronization task is executed, all microtasks are executed sequentially, with output
6,8
. - Execute the first
setTimeout
macro taskconsole.log(2)
and output2
. - When you encounter
process.nextTick
, put the function in the microtask queue. - When a
new promise
is encountered, immediately execute theconsole.log(4)
output4
, and putthe .then
callback function into the microtask queue. - After the macro task is executed, all microtasks are executed sequentially, with an output
of 3,5
. - Execute the second
setTimeout
macro taskconsole.log(9)
and output9
. - When you encounter
process.nextTick
, put the function in the microtask queue. - When a
new Promise
is encountered, immediately execute theconsole.log(11) output 11,
and putthe .then
callback function into the microtask queue. - When the macro task is executed, all microtasks are executed sequentially, with output
10
,12
.
Postscript
Recently encountered a related issue while dealing with react-redux's useSelector and useState hooks combined. Multiple setStates in the same useEffect, and the initial state and final state coincide, do not trigger the corresponding dependencies in other hooks. After using the new Promise(resolve=> setTimeout(resolve, 0)), the problem no longer occurs, which triggers the motivation to write this note. Speculation may be related to React's reconciliation mechanism, and it is necessary to dig deeper into React's code.