事件循环:微任务和宏任务
创新互联公司主营曹县网站建设的网络公司,主营网站建设方案,重庆APP开发,曹县h5微信小程序定制开发搭建,曹县网站营销推广欢迎曹县等地区企业咨询
浏览器中 JavaScript 的执行流程和 Node.js 中的流程都是基于 事件循环 的。
理解事件循环的工作方式对于代码优化很重要,有时对于正确的架构也很重要。
在本章中,我们首先介绍有关事件循环工作方式的理论细节,然后介绍该知识的实际应用。
事件循环
事件循环 的概念非常简单。它是一个在 JavaScript 引擎等待任务,执行任务和进入休眠状态等待更多任务这几个状态之间转换的无限循环。
引擎的一般算法:
1.当有任务时:
2.休眠直到出现任务,然后转到第 1 步。
当我们浏览一个网页时就是上述这种形式。JavaScript 引擎大多数时候不执行任何操作,它仅在脚本/处理程序/事件激活时执行。
任务示例:
……但是我们也可能想在任务执行期间展示一些东西,例如进度条。
如果我们使用 setTimeout 将繁重的任务拆分成几部分,那么变化就会被在它们之间绘制出来。
这看起来更好看:
现在 div 显示了 i 的值的增长,这就是进度条的一种。
用例 3:在事件之后做一些事情
在事件处理程序中,我们可能会决定推迟某些行为,直到事件冒泡并在所有级别上得到处理后。我们可以通过将该代码包装到零延迟的 setTimeout 中来做到这一点。
在 创建自定义事件[1] 一章中,我们看到过这样一个例子:自定义事件 menu-open 被在 setTimeout 中分派(dispatched),所以它在 click 事件被处理完成之后发生。
- menu.onclick = function() {
- // ...
- // 创建一个具有被点击的菜单项的数据的自定义事件
- let customEvent = new CustomEvent("menu-open", {
- bubbles: true
- });
- // 异步分派(dispatch)自定义事件
- setTimeout(() => menu.dispatchEvent(customEvent));
- };
宏任务和微任务
除了本章中所讲的 宏任务(macrotask) 外,还有在 微任务队列[2] 一章中提到的 微任务(microtask)。
微任务仅来自于我们的代码。它们通常是由 promise 创建的:对 .then/catch/finally 处理程序的执行会成为微任务。微任务也被用于 await 的“幕后”,因为它是 promise 处理的另一种形式。
还有一个特殊的函数 queueMicrotask(func),它对 func 进行排队,以在微任务队列中执行。
每个宏任务之后,引擎会立即执行微任务队列中的所有任务,然后再执行其他的宏任务,或渲染,或进行其他任何操作。
例如,看看下面这个示例:
- setTimeout(() => alert("timeout"));
- Promise.resolve()
- .then(() => alert("promise"));
- alert("code");
这里的执行顺序是怎样的?
更详细的事件循环图示如下(顺序是从上到下,即:首先是脚本,然后是微任务,渲染等):
微任务会在执行任何其他事件处理,或渲染,或执行任何其他宏任务之前完成。
这很重要,因为它确保了微任务之间的应用程序环境基本相同(没有鼠标坐标更改,没有新的网络数据等)。
如果我们想要异步执行(在当前代码之后)一个函数,但是要在更改被渲染或新事件被处理之前执行,那么我们可以使用 queueMicrotask 来对其进行安排(schedule)。
这是一个与前面那个例子类似的,带有“计数进度条”的示例,但是它使用了 queueMicrotask而不是 setTimeout。你可以看到它在最后才渲染。就像写的是同步代码一样:
总结
更详细的事件循环算法(尽管与 规范[3] 相比仍然是简化过的):
1.从 宏任务 队列(例如 "script")中出队(dequeue)并执行最早的任务。
2.执行所有 微任务:
3.执行渲染,如果有。
4.如果宏任务队列为空,则休眠直到出现宏任务。
5.转到步骤 1。
安排(schedule)一个新的 宏任务:
它可被用于将繁重的计算任务拆分成多个部分,以使浏览器能够对用户事件作出反应,并在任务的各部分之间显示任务进度。
此外,也被用于在事件处理程序中,将一个行为(action)安排(schedule)在事件被完全处理(冒泡完成)后。
安排一个新的 微任务:
在微任务之间没有 UI 或网络事件的处理:它们一个立即接一个地执行。
所以,我们可以使用 queueMicrotask 来在保持环境状态一致的情况下,异步地执行一个函数。
Web Workers:
对于不应该阻塞事件循环的耗时长的繁重计算任务,我们可以使用 Web Workers[4]。
这是在另一个并行线程中运行代码的方式。
Web Workers 可以与主线程交换消息,但是它们具有自己的变量和事件循环。
Web Workers 没有访问 DOM 的权限,因此,它们对于同时使用多个 CPU 内核的计算非常有用。
参考资料
[1]创建自定义事件: https://zh.javascript.info/dispatch-events
[2]微任务队列: https://zh.javascript.info/microtask-queue
[3]规范: https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model
[4]Web Workers: https://html.spec.whatwg.org/multipage/workers.html
[5]React 官方文档推荐,与 MDN 并列的 JavaScript 学习教程: https://zh-hans.reactjs.org/docs/getting-started.html#javascript-resources
本文转载自微信公众号「技术漫谈」,可以通过以下二维码关注。转载本文请联系技术漫谈公众号。
网页名称:图解JavaScript事件循环:微任务和宏任务
文章来源:http://www.shufengxianlan.com/qtweb/news28/121828.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联