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
2
3
4
5
6
7
// e.g.
// Why the timer is inaccurate,
// the timer will be executed after the for loop 2000 times,
// because it is single-threaded

for(let i=0;i<2000;i++){consol.log(1)}
settimeout(()=>{console.log(2)},0)

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
2
3
4
5
6
7
8
// After synchronous execution, before asynchronous execution
process.nextTick(()=>{
console.log(6)
})
// After the asynchronous code executes
setImmediate(()=>{
console.log(1)
})

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)

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
setImmediate(()=>{console.log(1)})

console.log(2)

setTimeout(()=>{console.log(3)},0)

setTimeout(()=>console.log(4),100)

console.log(5)

new Promise((resolve)=>{
console.log(6) //promise里面的是同步的
resolve()
}).then(()=>{
console.log(7) //then里面的是异步的
})
process.nextTick(()=>console.log(8))

// 25687314

// explain:
// First execute the synchronous code 2 5 6
// then execute nextTick 8
// Then execute the asynchronous code
// Execute microtasks first in asynchronous 7
// Then execute the macro task 3
// last execution setImmediate 1
// last execution 4 //Because it takes 100 milliseconds to enter the execution team,
//the entry is slow, so the last execution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
setImmediate(()=>{console.log(1)})
console.log(2)
setTimeout(()=>{console.log(3)},0)
setTimeout(()=>console.log(4),100)
new Promise((resolve)=>{
console.log(5) //The promise inside is synchronous
resolve()
}).then(()=>{
console.log(6) //Then inside is asynchronous
}).then(()=>{
console.log(7)
})
console.log(8)
process.nextTick(()=>console.log(9))
//258 9 67 314
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
setImmediate(()=>{console.log(1)})
console.log(2)
setTimeout(()=>{console.log(3)},0)
setTimeout(()=>console.log(4),100)
new Promise((resolve)=>{
console.log(5) //The promise inside is synchronous
//resolve() //then will only be executed when resolve is called,
//Without resolve, then will not execute
}).then(()=>{
console.log(6) //Then inside is asynchronous
}).then(()=>{
console.log(7)
})
console.log(8)
process.nextTick(()=>console.log(9))
//258 9 314

Promises and async functions

promise

then will be executed only when resolve is called, without resolve, then will not be executed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let p = new Promise((resolve)=>{
resolve('hi')
})
p.then((res)=>{
console.log(1)
console.log(res)
})
// 1 hi

let p = new Promise((resolve)=>{
// resolve('hi')
})
p.then((res)=>{
console.log(1)
console.log(res)
})
// print nothing


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
2
3
4
5
6
7
8
9
10
11
12
function fun1(){
return new Promsie((resolve)=>{
resolve(1)
})
}
// The async function is shorthand for the promise object
async function fun(){
return 1
}
let a = fun()
console.log(a)// Promise { 1 }
fun().then((data)=>{console.log(data)}) // 1

Add await to the async function and add a promise object after the await function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let p1 = new Promise((resolve)=>{
resolve(1)
})

let p2 = new Promise((resolve)=>{
resolve(2)
})

async function fun(){
// a = await
// Then add promise object await+promise object to get the value of resolve
let a = await p1
let b = await p2
console.log(a)
console.log(b)
}

fun()

// 1
// 2

The code below await is regarded as the code in then, which is the microtask in asynchronous

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
async function fun1(){
let data = await fun2()
console.log(data)
//The code below await is regarded as the code in then,
// which is the microtask in asynchronous
}

async function fun2(){
console.log(200)// synchronous code
return 100
}

fun1()

// 200
// 100
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

console.log(1)

async function async1(){
await async2()
console.log(2)
}

async function async2(){
console.log(3)
}
async1()

setTimeout(function(){
console.log(4)
},0)

new Promise(resolve=>{
console.log(5)
resolve()
}).then(function(){
console.log(6)
}).then(function(){
console.log(7)
})

console.log(8)

// 1358 267 4
// 1358 Synchronize 267 Micro task 4 Macro task

★ 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
console.log('script start')  //1

async function async1() {
await async2()
console.log('async1 end') //7
}
async function async2() {
console.log('async2 end') //2
}
async1()

setTimeout(function() {
console.log('setTimeout') //8
}, 0)

new Promise(resolve => {
console.log('Promise') //3
resolve()
})
.then(function() {
console.log('promise1') //5
})
.then(function() {
console.log('promise2') //6
})

console.log('script end') //4
// 1.script start =>2. async2 end => 3.Promise => 4.script end
【1234 is executed directly】
=> 5.promise1 => 6.promise2
【56 are all promise callback functions】
=>7.async1 end => 8.setTimeout
【7 await 8 settimeout】

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.