事件循环
事件循环(Event Loop)
事件循环流程
事件循环的作用:事件循环负责执行代码,收集和处理时间以及执行队列中的子任务。
为什么会有事件循环?
JavaScript是单线程(某一时刻只能执行一行代码),为了让耗时代码不阻塞其他代码运行,所以设计了时间循环模型。
概念:执行代码和收集异步任务的模型,在调用栈空闲,反复调用任务队列里的回调函数的执行机制,叫做事件循环。
1 |
|
以上代码在事件循环机制内运行过程:
①:执行console.log(1)
这是同步代码,进入调用栈,打印1,出栈。
②:执行第二段代码
1 |
|
这是异步代码,放入宿主环境(浏览器),待异步时间结束,返回任务队列。
③:执行console.log(3)
同步代码,进入调用栈,打印3,出栈。
④:执行第四段代码
1 |
|
这里也是异步代码,放入宿主环境(浏览器),待两秒后进入任务队列。
⑤: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 |
|
以上代码执行步骤:
①:读取<script>
标签 会将标签内代码推入宏队列中,调用栈空闲,进入<script>
。
②:读取console.log(1)
同步任务,放入调用栈,打印1,出栈。
③:读取
1 |
|
交给宿主环境(浏览器),将回调函数()=>{ console.log(2)}
放入宏任务队列中,
④:执行
1 |
|
Promise对象本身是同步的,Promise对象.then()的回调为异步的,执行console.log(3)
,进入调用栈,打印3,出栈。
⑤:执行:
1 |
|
这是Promise对象.then()的回调,会进入微任务队列。
⑥:执行console.log(5)
,进入调用栈,打印5,出栈。
⑦:此时<script>
标签内同步代码走完了,调用栈空闲,会先去调度微任务队列中的回调函数,执行console.log(result)
,打印4,出栈。
⑧:调用栈空闲,微任务队列调度完毕,再去宏任务队列调度回调函数,执行console.log(2)
,打印2,出栈。
至此任务队列回调函数全部执行完,所以打印的顺序为:1 3 5 4 2。