乐趣区

关于前端:面试官说说Event-Loop事件循环微任务宏任务

前言

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 执行原理的一些整顿和了解,心愿能给读者带来一些帮忙。如果有了解谬误或表述不当的中央,请斧正。
最初再给大家出一道题,能够把答案留在评论区
退出移动版