JAVASCRIPT中的《宏任务和微任务》

引用

JS机制

即时编译型和编译型语言

现在我们想象一个画面,一个已经完成的剧本摆在你面前。(就好比是一份需求文档)你需要用程序去把这个剧本里的画面去描绘出来。

编译型语言GO
首先我们需要把这个剧本整个读一遍。看看里面有没有错别字。然后如果有错别字,你就需要先修改完错别字再进行拍摄。并且尴尬的是拍摄完以后,发现某些镜头不满意,那么我们就需要先修改剧本,然后把这部电影从头开始再进行拍摄。

即时编译型语言JS
我们拿到剧本,看到第一页。emmm,剧情大概描述了“是一个大雪纷飞的场景”,ok,看到这你就可以直接拿起摄像机开始拍了,后面的剧情你不需要知道,没错,你可以看到哪里拍到哪里。从而引出上面我们提到的我们就是照稿子念的。看到哪念到哪里。如果有台词错误,我们可以随之马上修改,因为我们是边读边写的。(Chrome v8引擎在拿到剧本后所做的事情)

即时编译型语言 js 多了一个V8引擎翻译的环节。那么自然而然在执行速度上,就会略微逊色于编译型语言。

作者:韩振方
链接:https://juejin.cn/post/7172948984145641479
来源:稀土掘金

单线程

JavaScript 语言的一大特点就是单线程,也就是说同一个时间只能处理一个任务。

我们先假设 JS 是多线程看看会造成什么后果。现在 JS 不再是从上到下一行一行执行了,那么它在执行的期间,难免就会遇到同一时间。某一个线程接收到信号,我需要修改 body 的背景颜色为红色。而另外一个线程接受到信号,需要把整个 body 的背景颜色修改为蓝色,那么我们到底听谁的?(注意你是多线程,必定会存在同一时间点两个线程做同一件事,所以这里我们不能按照哪个线程在后面就听谁的想法去思考)

作者:韩振方
链接:https://juejin.cn/post/7172948984145641479
来源:稀土掘金

因此,为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,防止主线程的不阻塞,(事件循环)Event Loop 的方案应用而生。
JavaScript 处理任务是在等待任务、执行任务 、休眠等待新任务中不断循环中,也称这种机制为事件循环。

任务包括 script(整体代码)、 setTimeout、setInterval、DOM 渲染、DOM 事件、Promise、XMLHTTPREQUEST 等。

宏任务和微任务(MacroTask & MicroTask)

我们假设一个场景。我们去饭店吃饭。我们总是会把菜点完,然后把写着好几个菜的菜单拿给服务员,最好服务员一起送过去厨房做对吧?这时候有意思的点就来了,厨师👨‍🍳拿到菜单以后,一看《土豆炖牛肉》,《红烧肉》,《鸡蛋汤》。我感觉如果是一个正常的厨师师傅,都会给做菜顺序安排合理。“我不可能一看,第一行《土地炖牛肉》,后面的我压根不看,我就必须把这个菜先做了才能做后面的。”结果第一道菜50多分钟,客人干等着50分钟。

这时候就把《土地炖牛肉》开启高压锅放进了宏任务队列,转手把《鸡蛋汤》和《红烧肉》放进主线程去做。

当《土豆炖牛肉》快好的前几分钟,(注意!:这时候红烧肉和鸡蛋汤已经吃饱喝足被消灭了) 你突然又点了一个《拍黄瓜》🥜。师傅一看,《拍黄瓜》简单啊,虽然这个菜是最后上的,但是师傅直接给你放微任务啪啪啪几分钟给你搞好了,速度肯定比《土豆炖牛肉》剩下几分钟还要快,自然而然你就先吃到了《拍黄瓜》。

作者:韩振方
链接:https://juejin.cn/post/7176266687241519162
来源:稀土掘金

因此事件循环顺序是:

  1. 进入到 script 标签,就进入到了第一次事件循环.
  2. 遇到同步代码,立即执行
  3. 遇到宏任务,放入到宏任务队列里.
  4. 遇到微任务,放入到微任务队列里.
  5. 执行完所有同步代码
  6. 执行微任务代码
  7. 微任务代码执行完毕,本次队列清空
  8. 更新 DOM 渲染
  9. 寻找下一个宏任务,重复步骤1

常见任务类型

宏任务(Macrotask)微任务(Microtask)
setTimeoutrequestAnimationFrame(有争议)
setIntervalMutationObserver(浏览器环境)
MessageChannelPromise.[then/catch/finally]
I/O,事件队列process.nextTick(Node环境)
setImmediate(Node环境)queueMicrotask
script(整体代码块)

注意:

Js引擎为了让microtask尽快的输出,做了一些优化,连续的多个then(3个)如果没有reject或者resolve会交替执行then而不至于让一个堵太久完成用户无响应,不单单v8这样其他引擎也是这样,因为其实promuse内部状态已经结束了。这块在v8源码里有完整的体现