用于显式添加微任务或宏任务的 Javascript API

标签 javascript asynchronous w3c vm-implementation

从我对 javascript 虚拟机如何工作的整体理解来看,我可以清楚地看到微任务/宏任务的概念发挥着重要作用。

<小时/>

这是我对此的理解:

  • VM“轮转”是将一个宏任务从 VM 宏任务队列中拉出并执行它的事实。
  • VM轮换期间,可以将微任务添加到当前宏任务的微任务队列中。
  • 微任务可以将其他微任务推送到当前宏任务的微任务队列中。
  • 当微任务队列为空时,虚拟机轮次将结束。
<小时/>

这是我的问题的要点:

为什么没有明确的 API 来操作这两个队列。

类似于

  • pushToMacroTask( 函数 )
  • pushToMicroTask( 函数 )

实际上,操作这些队列的唯一方法似乎是使用 setTimeout() 将任务添加到宏任务队列,使用 Promises 将任务添加到微任务队列任务队列...

我对此表示同意,但这并没有给我们一个有意义的 API,你不觉得吗?

这个概念是否应该对 JS 开发人员保持“隐藏”并且仅在某些 hacky 情况下使用?

您知道是否有关于该主题的任何 W3C 规范吗?

所有 VM 引擎都以相同的方式实现这个概念吗?

我很高兴听到有关这方面的故事和意见。

谢谢!

最佳答案

是否有任何有关微/宏任务的 W3C 规范?

W3C 谈到 task queues :

When a user agent is to queue a task, it must add the given task to one of the task queues of the relevant event loop. All the tasks from one particular task source (e.g. the callbacks generated by timers, the events dispatched for mouse movements, the tasks queued for the parser) must always be added to the same task queue, but tasks from different task sources may be placed in different task queues.

EcmaScript2015 谈到 Job Queues ,并要求至少支持两个:

  • ScriptJobs: Jobs that validate and evaluate ECMAScript Script and Module source text.
  • PromiseJobs: Jobs that are responses to the settlement of a Promise.

此语言定义不知道可能的事件循环,但可以想象保留一个或多个作业队列以与 W3C 规范中提到的任务队列一起使用。浏览器将根据链接到作业队列的 W3C 任务队列规范触发 setTimeout 回调,而 Promise必须直接使用作业队列规范(不是任务队列)。还提到了代理可以将任务注入(inject)作业队列:

Alternatively, [an implementation] might choose to wait for a some implementation specific agent or mechanism to enqueue new PendingJob requests.

EcmaScript 规范不强制规定服务不同作业队列的优先级:

This specification does not define the order in which multiple Job Queues are serviced. An ECMAScript implementation may interweave the FIFO evaluation of the PendingJob records of a Job Queue with the evaluation of the PendingJob records of one or more other Job Queues.

因此,这里似乎没有严格要求 promise 履行应在 setTimeout 任务之前提供服务。但网络超文本应用技术工作组 [WHATWG] 在涵盖 event loops 时更加具体。 :

Each event loop has a microtask queue. A microtask is a task that is originally to be queued on the microtask queue rather than a task queue.

[2019年新增]:同时Living HTML Standard [WHATWG] 现在包括以下内容:

8.6 Microtask queuing

self.queueMicrotask(callback)

Queues a microtask to run the given callback.

The queueMicrotask(callback) method must queue a microtask to invoke callback, and if callback throws an exception, report the exception.

The queueMicrotask() method allows authors to schedule a callback on the microtask queue. This allows their code to run after the currently-executing task has run to completion and the JavaScript execution context stack is empty, but without yielding control back to the event loop, as would be the case when using, for example, setTimeout(f, 0).

所有 VM 引擎都以相同的方式实现这一点吗?

从历史上看,不同的浏览器实现会导致不同的执行顺序。这个article从 2015 年开始,读起来可能会很有趣,看看它们有多么不同:

Some browsers [...] are running promise callbacks after setTimeout. It's likely that they're calling promise callbacks as part of a new task rather than as a microtask.

Firefox and Safari are correctly exhausting the microtask queue between click listeners, as shown by the mutation callbacks, but promises appear to be queued differently. [...] With Edge we've already seen it queues promises incorrectly, but it also fails to exhaust the microtask queue between click listeners, instead it does so after calling all listeners.

从那时起,几个问题已经得到解决和协调。

但请注意,不必有一个个微任务队列,也不必有一个个宏任务队列。可以有多个队列,每个队列都有自己的优先级。

有意义的 API

实现你建议的两个功能当然不是那么困难:

let pushToMicroTask = f => Promise.resolve().then(f);
let pushToMacroTask = f => setTimeout(f);

pushToMacroTask(() => console.log('Macro task runs last'));
pushToMicroTask(() => console.log('Micro task runs first'));

[2019] 现在我们有了 queueMicrotask(),有一个 native 实现。下面是一个演示,将该方法与上面基于 Promise 的实现进行比较:

let queuePromisetask = f => Promise.resolve().then(f);
let queueMacrotask= f => setTimeout(f);

queueMicrotask(() => console.log('Microtask 1'));
queueMacrotask(() => console.log('Macro task'));
queuePromisetask(() => console.log('Promise task'));
queueMicrotask(() => console.log('Microtask 2'));

关于用于显式添加微任务或宏任务的 Javascript API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41075724/

相关文章:

css - Facebook LIKE 按钮和 W3C 验证失败

java - 无法调用 StAXOMBuilder() 类中的 protected 方法

html - 是否需要在<table>中添加cellspacing ="0"cellpadding ="0"?

keyup 函数上的 JavaScript

javascript - Javascript 中的行索引

java - SQL Server JDBC 驱动程序是否支持异步操作?

c# - 在 MVC 应用程序中使用 await Task<T> async 挂起 Controller

javascript - express.js,如果删除下一个参数,错误处理程序将不起作用

javascript - 如何将 Highcharts 图表中的列移至右侧?

c++ - 异步 COM C++ 调用