What is an event loop in JavaScript ?
Synchronize and Asynchronous
Synchronize:Execute in order
Asynchronous:Execute a part first, wait for the result and then execute the subsequent code
- 1.setTimeout
- 2.Ajax
- 3.IO
After the synchronous program is executed, execute the asynchronous program
Single Thread
Js is single-threaded, one task can only be executed after another task is executed
1 | // e.g. |
process.nextTick and setImmediate in Nodejs
- 1.Synchronize
- 2.nextTick
- 3.Asynchronous
- 4.setImmediate(The execution of the current event ends sequentially, that is, it will be executed when the task team is empty)
1 | // After synchronous execution, before asynchronous execution |
What is an event loop
The timer is inaccurate: the following code is inserted into the task queue after the time is up. And it starts running this after waiting for the previous ones in the queue to finish running.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
setImmediate(()=>{console.log(1)})
// After the current event loop ends,
// 5 is not executed because 5 has not been put in the task queue at this time
process.nextTick(()=>{console.log(2)})
console.log(3)
setTimeout(()=>{console.log(4)},0)
setTimeout(()=>{console.log(5)},1000)
setTimeout(()=>{console.log(6)},0)
console.log(7)
1 | setImmediate(()=>{console.log(1)}) |
Macrotasks and Microtasks
Macrotasks
- setTimeout
- ajax
- read file
- script
- mouse events, keyboard events
Microtasks
- promise.then
- process.nextTick
Order of execution
- 1.synchronization program
- 2.Process.nextTick
- 3.Micro task
- 4.Macro task
- 5.setImmediate
1 | setImmediate(()=>{console.log(1)}) |
1 | setImmediate(()=>{console.log(1)}) |
1 | setImmediate(()=>{console.log(1)}) |
Promises and async functions
promise
then will be executed only when resolve is called, without resolve, then will not be executed
1 | let p = new Promise((resolve)=>{ |
The return value of async is a promise object,
The parameter passed by then is the value of return,
The async function is shorthand for the promise object
1 | function fun1(){ |
Add await to the async function and add a promise object after the await function
1 | let p1 = new Promise((resolve)=>{ |
The code below await is regarded as the code in then, which is the microtask in asynchronous
1 | async function fun1(){ |
1 |
|
★ Execution stack
Execute the code. According to the principle of first in, last out, the function executed later will pop the stack first;
Because the last executed function, after execution, it pops up;
Then the following is the previous function, one by one in order to pop up the;
★ Event Loop in the browser
When executing JS code, it is actually putting functions into the execution stack. When encountering asynchronous code, it will be suspended and added to the Task (there are many kinds of Task) queues when it needs to be executed. Once the execution stack is empty, Event Loop will take out the code that needs to be executed from the Task queue and put it into the execution stack for execution, so in essence, asynchronous or synchronous behavior in JS.
Event Loop
Different task sources will be assigned to different Task queues. Task sources can be divided into microtask (microtask) and macrotask (macrotask). In the ES6 specification, microtasks are called jobs
and macrotasks are called tasks
. Let’s look at the execution sequence of the following code:
1 | console.log('script start') //1 |
await
It is regarded as a sign of giving up the thread.
Then when all the synchronous codes are executed, all asynchronous codes will be executed, and then the resolve
function of the returned Promise
will be executed at the position of await
, which will throw resolve
to the micro In the task queue, execute the callback in then
next. When the callbacks in the two then
are all executed, it will return to the position of await
to process the return value. At this time, you can regard it as Promise.resolve(return value).then()
, and then the code after await
is all wrapped into the then
callback, so console.log('async1 end')
will be executed first in setTimeout
.
So the Event Loop execution sequence is as follows:
- First execute the synchronous code, which belongs to the macro task, [synchronous code in the script macro task]
- After all synchronous codes are executed, the execution stack is empty, check whether there is any asynchronous code to execute
- Execute all microtasks
- When all microtasks are executed, render the page if necessary
- Then start the next round of Event Loop to execute the asynchronous code in the macro task, which is the callback function in
setTimeout
So although setTimeout
is written before Promise
in the above code, but because Promise
belongs to microtasks and setTimeout
belongs to macrotasks, the above will be printed.
Microtasks include process.nextTick
, promise
, MutationObserver
.
Macro tasks include script
, setTimeout
, setInterval
, setImmediate
, I/O
, UI rendering
.
Many people here have a misunderstanding, thinking that micro-tasks are faster than macro-tasks, but it is actually wrong. Because script
is included in the macrotask, the browser will execute a macrotask first, and then execute the microtask if there is asynchronous code next.
main thread main thread and call-stack call stack
Javascript has a main thread main thread and a call-stack call stack (execution stack), and all tasks will be placed on the call stack to wait for the main thread to execute.
JS call stack
The JS call stack is a last-in-first-out data structure. When a function is called, it will be added to the top of the stack. After the execution is completed, the function will be removed from the top of the stack until the stack is emptied.
synchronous task, asynchronous task
Tasks in JavaScript single thread are divided into synchronous tasks and asynchronous tasks. Synchronous tasks will be queued in the call stack [last in first out] in order to wait for the main thread to execute, and asynchronous tasks will add the registered callback function to the task queue (message queue) after the asynchronous result is obtained [different from the call stack Another thing, when the main thread is idle in FIFO], that is, when the stack [LIFO synchronization task] is emptied, it is read into the stack [call stack] and waits for the main thread to execute. The task queue is a first-in, first-out data structure.
Event Loop
The synchronization tasks in the call stack are all executed, and the stack is cleared, which means that the main thread is idle. At this time, it will go to the task queue to read a task in order and put it into the stack for execution. Every time the stack is emptied, it will read whether there are tasks in the task queue, and if there are any tasks, it will be read and executed, and the read-execution operation has been cyclically, forming an event loop.