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 处理任务是在等待任务、执行任务 、休眠等待新任务中不断循环中,也称这种机制为事件循环。
- 主线程中的任务执行完后,才执行任务队列中的任务
- 有新任务到来时会将其放入队列,采取先进先执行的策略执行队列中的任务
- 比如多个
setTimeout
同时到时间了,就要依次执行
任务包括 script(整体代码)、 setTimeout、setInterval、DOM 渲染、DOM 事件、Promise、XMLHTTPREQUEST 等。
宏任务和微任务(MacroTask & MicroTask)
我们假设一个场景。我们去饭店吃饭。我们总是会把菜点完,然后把写着好几个菜的菜单拿给服务员,最好服务员一起送过去厨房做对吧?这时候有意思的点就来了,厨师👨🍳拿到菜单以后,一看《土豆炖牛肉》,《红烧肉》,《鸡蛋汤》。我感觉如果是一个正常的厨师师傅,都会给做菜顺序安排合理。“我不可能一看,第一行《土地炖牛肉》,后面的我压根不看,我就必须把这个菜先做了才能做后面的。”结果第一道菜50多分钟,客人干等着50分钟。
这时候就把《土地炖牛肉》开启高压锅放进了宏任务队列,转手把《鸡蛋汤》和《红烧肉》放进主线程去做。
当《土豆炖牛肉》快好的前几分钟,(注意!:这时候红烧肉和鸡蛋汤已经吃饱喝足被消灭了) 你突然又点了一个《拍黄瓜》🥜。师傅一看,《拍黄瓜》简单啊,虽然这个菜是最后上的,但是师傅直接给你放微任务啪啪啪几分钟给你搞好了,速度肯定比《土豆炖牛肉》剩下几分钟还要快,自然而然你就先吃到了《拍黄瓜》。
作者:韩振方
链接:https://juejin.cn/post/7176266687241519162
来源:稀土掘金
因此事件循环顺序是:
- 进入到 script 标签,就进入到了第一次事件循环.
- 遇到同步代码,立即执行
- 遇到宏任务,放入到宏任务队列里.
- 遇到微任务,放入到微任务队列里.
- 执行完所有同步代码
- 执行微任务代码
- 微任务代码执行完毕,本次队列清空
- 更新 DOM 渲染
- 寻找下一个宏任务,重复步骤1
常见任务类型
宏任务(Macrotask) | 微任务(Microtask) |
---|---|
setTimeout | requestAnimationFrame(有争议) |
setInterval | MutationObserver(浏览器环境) |
MessageChannel | Promise.[then/catch/finally] |
I/O,事件队列 | process.nextTick(Node环境) |
setImmediate(Node环境) | queueMicrotask |
script(整体代码块) |
注意:
Js引擎为了让microtask尽快的输出,做了一些优化,连续的多个then(3个)如果没有reject或者resolve会交替执行then而不至于让一个堵太久完成用户无响应,不单单v8这样其他引擎也是这样,因为其实promuse内部状态已经结束了。这块在v8源码里有完整的体现