写点什么

面试官:说说 Event Loop 事件循环、微任务、宏任务

作者:loveX001
  • 2022-11-15
    浙江
  • 本文字数:1804 字

    阅读完需:约 6 分钟

前言

JS 是一门单线程语言,单线程就意味着,所有的任务需要排队,前一个任务结束,才会执行下一个任务。这样所导致的问题是:如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的觉。为了解决这个问题,JS 中出现了同步和异步。他们的本质区别是:一条流水线上各个流程的执行顺序不同。在讲 JS 任务执行机制前,先要了解一下什么是同步任务与异步任务。


同步任务:即主线程上的任务,按照顺序由上⾄下依次执⾏,当前⼀个任务执⾏完毕后,才能执⾏下⼀个任务。


异步任务:不进⼊主线程,⽽是进⼊任务队列的任务,执行完毕之后会产生一个回调函数,并且通知主线程。当主线程上的任务执行完后,就会调取最早通知自己的回调函数,使其进入主线程中执行。

1. 事件循环 Event Loop 概念介绍

  • 事件循环 Event Loop 又叫事件队列,两者是一个概念


事件循环指的是 js 代码所在运行环境(浏览器、nodejs)编译器的一种解析执行规则。事件循环不属于 js 代码本身的范畴,而是属于 js 编译器的范畴,在 js 中讨论事件循环是没有意义的。换句话说,js 代码可以理解为是一个人在公司中具体做的事情, 而 事件循环 相当于是公司的一种规章制度。 两者不是一个层面的概念。

2. 微任务、宏任务概念介绍

  1. 微任务与宏任务就属于 js 代码的范畴

  2. js 代码主要分为两大类: 同步代码、异步代码

  3. 异步代码又分为:微任务与宏任务


3. 事件循环 Event Loop 执行机制

  • 1.进入到 script 标签,就进入到了第一次事件循环.

  • 2.遇到同步代码,立即执行

  • 3.遇到宏任务,放入到宏任务队列里.

  • 4.遇到微任务,放入到微任务队列里.

  • 5.执行完所有同步代码

  • 6.执行微任务代码

  • 7.微任务代码执行完毕,本次队列清空

  • 寻找下一个宏任务,重复步骤 1

  • 以此反复直到清空所以宏任务,这种不断重复的执行机制,就叫做事件循环


画了一张图来描述事件循环


4.易错点

(1). promise 本身是一个同步的代码(只是容器),只有它后面调用的 then()方法里面的回调才是微任务



(2). await 右边的表达式还是会立即执行,表达式之后的代码才是微任务, await 微任务可以转换成等价的 promise 微任务分析



(3). script 标签本身是一个宏任务, 当页面出现多个 script 标签的时候,浏览器会把 script 标签作为宏任务来解析



参考 前端进阶面试题详细解答


看到这里,对事件循环应该有所了解了,给大家看几道面试题。


一.


1.先执行主线程上的 log(1)


2.当有两个 await 时,只有第一个 await 右边的代码会立即执行 log(4),后面的几行代码都会放入微任务队列中。


3.执行主线程上的 log(6)


4.执行第 4 行至第 6 行的微任务


二.



1.先执行主线程上的 1,5,7


2.主线程的同步任务执行完毕后,会先执行微任务。执行 Promise 的 then 方法里的代码,打印 6


3.微任务执行完毕后,最后执行定时器里的宏任务,打印 2,3,4


三.



1.先执行主线程上的同步代码,打印 1


2.执行第 9 行的函数,进⼊async1 内部,async1 其实是声明了⼀个 promise,promise 是同步代码,会顺序执⾏打印 async2 函数里的 4 ,只有.then⾥⾯的代码会加⼊微任务队列⾥,这⾥相当于执⾏了 async2()之后,再将后面的代码加⼊⼀个微任务队列中。


3.回主线程中,遇到 setTimeout(),加⼊到宏任务队列


4.主线程继续往后执⾏,前⾯说过,promise 是同步代码,.then 后⾯的回调会加⼊微任务队列,所以会打印 13⾏的 7


5.主线程执⾏完成,开始执⾏微任务队列内的任务,遵循先进先出的原则,打印第四⾏的 2。然后接着执行第 5 行第二个 awaite 右边的代码,打印 5。第 6 行这个时候就被加入微任务队列。


6.接着会执行第二个微任务,也就是 16 行代码,打印 8。第 17 行的 then 这个时候也会加入微任务队列。再依次执行第 6 行和第 17 行的两个微任务,打印 3 和 9


7.微任务执⾏结束,开始执⾏宏任务 setTimeout,打印 11⾏的 6.

总结

  1. 所有同步任务都在主线程上执行,形成一个执行栈(call stack)。

  2. 遇到异步任务, 进入异步处理模块并注册回调函数; 等到指定的事件完成(如 ajax 请求响应返回, setTimeout 延迟到指定时间)时,异步处理模块会将这个回调函数移入异步任务队列。

  3. 当栈中的代码执行完毕,执行栈中的任务为空时,主线程会先检查微任务队列中是否有任务,如果有,就将微任务队列中的所有任务依次执行,直到微任务队列为空; 之后再检查宏任务队列中是否有任务,如果有,则取出第一个宏任务加入到执行栈中,之后再清空执行栈,检查微任务,以此循环,直到全部的任务都执行完成。以上就是我对 JS 执行原理的一些整理和理解,希望能给读者带来一些帮助。如果有理解错误或表述不当的地方,请指正。

最后再给大家出一道题,可以把答案留在评论区


用户头像

loveX001

关注

还未添加个人签名 2022-09-01 加入

还未添加个人简介

评论

发布
暂无评论
面试官:说说Event Loop事件循环、微任务、宏任务_JavaScript_loveX001_InfoQ写作社区