background
Event loop is a very important concept, which essentially refers to the running mechanism of the computer. JavaScript language adopts this mechanism. As we all know that JavaScript is single-threaded, why is it designed to be single-threaded? In fact, a few years ago, Ruan Yifeng teacher gave the answer, such benefits improve efficiency, do only one thing at a time. However, this also leads to a problem: all tasks need to be queued, and only the previous task can be executed. The designers of JavaScript language realized that this was not possible, so they divided all tasks into two types: synchronous tasks and asynchronous tasks maintain a task queue, and the main thread reads tasks from the task queue. The whole process is a continuous loop, which is called Event loop.
Why do we need to know about it
In the real world, knowing Event loop will help you analyze an asynchronous sequence problem, but it will also help you understand the internal mechanics of browsers and Nodes, and most importantly, it is a guaranteed interview question.
Browser implementation
The main tasks in the browser are divided into two types: synchronous tasks and asynchronous tasks.
Asynchronous tasks: Macrotask and Microtask. The tasks in the macro and Microtask queue alternate with each other as the tasks are pushed, pushed, queued and queued. To get an idea of this concept, use a pseudo-code:
// Task queue (first in first out)
let EventLoop = [];
let event;
// Execute "forever"
while (true) {
/ / a tack
if (EventLoop.length > 0) {
// Get the next event in the queue
event = EventLoop.shift();
// Now execute the next event
try {
event();
} catch (error) {
// Report an errorreportError(error); }}}Copy the code
Common MacroTasks
- The script tag
- setTimeout
- setInterval
- SetImmediate (In Node environment)
- requestAnimationFrame
Microtasks
- Process.nexttick (in Node environment)
- Promise callback includes :()
- MutationObserver
Once you know the concept, let’s look at a simple example to start with. You don’t have to know the final print result, you should be aware of the current code which are macro tasks, micro tasks
Chestnut 🌰
console.log('start'); / / no. 1
setTimeout(function () { / / no. 2
console.log('timeout');
}, 0);
Promise.resolve().then(function () { / / no. 3
console.log('promise');
});
console.log('end'); / / no. 4
Copy the code
Implementation process:
Process:
- The runtime recognizes the log method, pushes it, and then performs input
start
Out of the stack - Identify methods whose setTimeout is asynchronous (
Macro task
), put the anonymous callback function in the queue (macro task) and execute it in the next event loop. - Runs into
promise callback
, belong to (Micro tasks
), placed in the (microtask) queue. - The runtime recognizes the log method, pushes it, and then performs input
end
Out of the stack. - When the stack is empty after the main process finishes executing, the item at the head of the queue is taken out and printed
promise
, until there is no data in the queue - Loop through the next queue and print out on a first-in, first-out basis
timeout
Types of tasks
- Number 1: Synchronization task
- Number 2: macro task
- Number 3: Microtasks
- Number 4: Synchronization task
Results of execution:
start
end
promise
timeout
Copy the code
Strike while the iron is hot
console.log('start'); / / no. 1
new Promise(function(resolve, rejected){
console.log('Promise-1') / / no. 2
resolve()
}).then(function(res){ / / no. 3
console.log('Promise-2')})setTimeout(function () { / / no. 4
console.log('timeout');
}, 0);
Promise.resolve().then(function () { / / no. 5
console.log('promise');
});
console.log('end'); / / no. 6
Copy the code
Implementation process:
The only difference between this example and the previous one is the addition of a new Promise (number 2) which prints console.log(‘ promise-1 ‘). Note that only the Promise callback is for asynchronous tasks. But inside a function is a synchronization task, and that’s where most people get confused.
Results:
start
Promise-1
end
Promise-2
promise
timeout
Copy the code
Unlock the Event Loop completely
console.log('1');
async function foo() {
console.log('13');
await bar();
console.log('15');
}
function bar() {
console.log('14');
}
setTimeout(function () {
console.log('2');
new Promise(function (resolve) {
console.log('4');
resolve();
}).then(function () {
console.log('5');
});
});
new Promise(function (resolve) {
console.log('7');
resolve();
}).then(function () {
console.log('8');
});
setTimeout(function () {
console.log('9');
new Promise(function (resolve) {
console.log('11');
resolve();
}).then(function () {
console.log('12');
});
});
foo();
Copy the code
Implementation process:
First event loop:
Parsing the entire JavaScript file is in a macro task that is printed directly when encountered with synchronization console.log(‘1’). If function is not called, it will skip to the first setTimeout and Queue macro1. Then parse to the new Promise and execute the console.log(‘7′) code inside, then plug into the (microtask)Queue and mark micro1, After that, setTimeout is inserted again into the Queue labeled macro2, and finally foo() is promoted to async, which simply indicates that the current function is asynchronous. Does not affect function execution console.log(’13’), encountered awiat bar execute bar console.log(’14’), aWIat with block after the code, put microtask list to mark micro2;
Current macro task:
- The synchronization code executed is:
[1, 7, 13, 14]
- Micro task Queue:
[8, 15]
- Macro task Queue:
[macro1, macro2]
The input results are 1, 7, 13, 14, 8, and 15
Empty the current microtask queue at this pointmicro = []
Second event loop: The first setTimeout output console.log(‘2’) is executed, the new promise is entered into console.log(‘4’), and then is re-marked in micro as micro1. Output console.log(‘5’) to check that there are no other microtasks
Current macro task:
- The synchronization code executed is:
[2, 4]
- Micro task Queue:
[5]
- Macro task Queue:
[macro2]
The input results are 2, 4, and 5
Empty the current microtask queue at this pointmicro = []
Third event loop: Same order as the previous event loop
Current macro task:
- The synchronization code executed is:
[9, 11]
- Micro task Queue:
[12]
- Macro task Queue:
[]
The input results are 9, 11, and 12
Clear all task queues
conclusion
It is important to distinguish between macro tasks and micro tasks in the event cycle. Knowing the sequence is the only way to know the order in which the current events will be executed.