事件循环

事件循环(Event Loop)

事件循环流程

事件循环的作用:事件循环负责执行代码,收集和处理时间以及执行队列中的子任务。

为什么会有事件循环?

JavaScript是单线程(某一时刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,所以设计了时间循环模型。

概念:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里的回调函数的执行机制,叫做事件循环。

1
2
3
4
5
6
7
8
9
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
console.log(3)
setTimeout(() => {
console.log(4)
}, 2000)
console.log(5)

以上代码在事件循环机制内运行过程:

事件循环具体过程

①:执行console.log(1)这是同步代码,进入调用栈,打印1,出栈。

②:执行第二段代码

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

这是异步代码,放入宿主环境(浏览器),待异步时间结束,返回任务队列。

③:执行console.log(3)同步代码,进入调用栈,打印3,出栈。

④:执行第四段代码

1
2
3
setTimeout(() => {
console.log(4)
}, 2000)

这里也是异步代码,放入宿主环境(浏览器),待两秒后进入任务队列。

⑤:console.log(5)同步代码,放入调用栈,打印5,出栈。

此时调用栈空闲,反复调用任务队列里的回调函数,先调用第二段代码到调用栈,打印2,出栈,再调用第四段代码到调用栈,打印4,再出栈。

所以打印数字的顺序:1 3 5 2 4。

宏任务和微任务

任务队列又分为宏任务(macro-task)微任务(micro-task)

宏任务:浏览器执行的异步代码。例如:JS 执行脚本事件,setTimeout/setInterval,AJAX请求完成

事件,用户交互事件等。

微任务:JS引擎执行的异步代码。例如:Promise对象.then()的回调。

代码执行:

1.执行第一个 <script> 脚本事件宏任务里面同步代码。

2.遇到 宏任务/微任务 交给宿主环境,有结果回调函数进入对应队列。

3.当执行调用栈空闲时,先清空微任务队列,再清空宏任务队列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
console.log(1)
setTimeout(()=>{
console.log(2)
},0)
const p new Promise((resolve,reject)=>{
console.log(3)
resolve(4)
})
p.then(result =>{
console.log(result)
})
console.log(5)
</script>
宏任务微任务

以上代码执行步骤:

①:读取<script>标签 会将标签内代码推入宏队列中,调用栈空闲,进入<script>

②:读取console.log(1)同步任务,放入调用栈,打印1,出栈。

③:读取

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

交给宿主环境(浏览器),将回调函数()=>{ console.log(2)}放入宏任务队列中,

④:执行

1
2
3
4
const p new Promise((resolve,reject)=>{
console.log(3)
resolve(4)
})

Promise对象本身是同步的,Promise对象.then()的回调为异步的,执行console.log(3),进入调用栈,打印3,出栈。

⑤:执行:

1
2
3
p.then(result =>{
console.log(result)
})

这是Promise对象.then()的回调,会进入微任务队列。

⑥:执行console.log(5),进入调用栈,打印5,出栈。

⑦:此时<script>标签内同步代码走完了,调用栈空闲,会先去调度微任务队列中的回调函数,执行console.log(result),打印4,出栈。

⑧:调用栈空闲,微任务队列调度完毕,再去宏任务队列调度回调函数,执行console.log(2),打印2,出栈。

至此任务队列回调函数全部执行完,所以打印的顺序为:1 3 5 4 2。


事件循环
http://example.com/2023/09/25/事件循环/
作者
AlongSunsea
发布于
2023年9月25日
许可协议