事件循环上下文中微任务和宏任务之间的区别

新手上路,请多包涵

我刚读完 Promises/A+ 规范,无意中发现了术语微任务和宏任务:请参阅 http://promisesaplus.com/#notes

我以前从未听说过这些术语,现在我很好奇它们有什么区别?

我已经尝试在网上找到一些信息,但我所找到的只是 w3.org 档案中的这篇文章(这并没有向我解释不同之处):http: //lists.w3.org/Archives /Public/public-nextweb/2013Jul/0018.html

此外,我发现了一个名为“macrotask”的 npm 模块: https ://www.npmjs.org/package/macrotask 同样,具体的区别并不清楚。

我所知道的是,它与事件循环有关,如 https://html.spec.whatwg.org/multipage/webappapis.html#task-queuehttps://html.spec.whatwg 中所述 .org/multipage/webappapis.html#perform-a-microtask-checkpoint

我知道理论上我应该能够自己提取差异,鉴于此 WHATWG 规范。但我相信其他人也可以从专家给出的简短解释中受益。

原文由 NicBright 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 267
2 个回答

事件循环的一次循环将 恰好宏任务队列 中处理一个任务(这个队列在 WHATWG 规范 中简称为 _任务队列_)。在这个宏任务完成后,所有可用 的微任务 将被处理,即在同一个复飞周期内。在处理这些微任务的同时,它们可以排队更多的微任务,这些微任务将一个接一个地运行,直到微任务队列耗尽。

这样做的实际后果是什么?

如果一个 微任务 递归地将其他微任务排入队列,则可能需要很长时间才能处理下一个宏任务。这意味着,您最终可能会遇到阻塞的 UI,或者您的应用程序中一些已完成的 I/O 空闲。

然而,至少关于 Node.js 的 process.nextTick 函数(它对微任务进行 排队),通过 process.maxTickDepth 内置了针对此类阻塞的保护。此值设置为默认值 1000,在达到此 限制 后减少微任务的进一步处理,从而允许处理下一个 宏任务

那么什么时候使用什么?

基本上, 您需要以同步方式异步执行操作时(即当您说 _在最近的将来执行此(微)任务时),请使用微任务_。否则,请坚持使用 macrotasks

例子

宏任务: setTimeoutsetIntervalsetImmediaterequestAnimationFrameI/O 、UI渲染

微任务: process.nextTickPromisesqueueMicrotaskMutationObserver

原文由 NicBright 发布,翻译遵循 CC BY-SA 4.0 许可协议

规范 中的基本概念:

  • 一个事件循环有一个或多个任务队列。(任务队列是宏任务队列)
  • 每个事件循环都有一个微任务队列。
  • 任务队列 = 宏任务队列 != 微任务队列
  • 任务可能会被推入宏任务队列或微任务队列
  • 当一个任务被推入队列(微/宏)时,我们意味着准备工作已经完成,所以现在可以执行任务。

而事件循环过程模型如下:

调用堆栈 为空时,执行以下步骤-

  1. 选择任务队列中最旧的任务(任务 A)
  2. 如果任务A为空(意味着任务队列为空),跳转到第6步
  3. 将“当前运行的任务”设置为“任务 A”
  4. 运行“任务A”(意味着运行回调函数)
  5. 将“当前运行的任务”设置为空,删除“任务 A”
  6. 执行微任务队列
    • (a).选择微任务队列中最早的任务(任务x)
    • (b).如果任务 x 为空(意味着微任务队列为空),跳转到步骤 (g)
    • ©.将“当前运行的任务”设置为“任务x”
    • (d).运行“任务x”
    • (e). 将“当前正在运行的任务”设置为空,删除“任务 x”
    • (f).选择微任务队列中下一个最旧的任务,跳转到步骤(b)
    • (g).完成微任务队列;
  7. 跳转到第 1 步。

一个简化的过程模型如下:

  1. 运行宏任务队列中最早的任务,然后将其删除。
  2. 运行微任务队列中的所有可用任务,然后删除它们。
  3. 下一轮:运行宏任务队列中的下一个任务(跳转第2步)

要记住的事情:

  1. 当一个任务(在宏任务队列中)正在运行时,可能会注册新事件。因此可能会创建新任务。下面是两个新创建的任务:
    • promiseA.then() 的回调是一个任务
      • promiseA 被解决/拒绝:任务将被推入当前轮事件循环中的微任务队列。
      • promiseA is pending:任务将在下一轮事件循环中被推入微任务队列(可能是下一轮)
    • setTimeout(callback,n)的回调是一个task,即使n为0也会被push到macrotask队列中;
  2. 微任务队列中的任务将在本轮运行,而宏任务队列中的任务必须等待下一轮事件循环。
  3. 我们都知道”click”,“scroll”,“ajax”,“setTimeout”…的回调是任务,但是我们也应该记住script标签中的js代码作为一个整体也是一个任务(宏任务)。

原文由 wengeezhang 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题