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

Cle*_*ent 2 javascript w3c asynchronous vm-implementation

根据我对javascript虚拟机如何工作的全球理解,我可以清楚地看到微任务/宏任务的概念起着重要作用.


以下是我对此的理解:

  • VM'turn'是将一个宏任务从VM宏任务队列中拉出来并执行它的事实.
  • 在VM转弯期间,可以将微任务添加到当前宏任务的微任务队列中.
  • 微任务可以将其他微任务推送到当前宏任务的微任务队列.
  • 当微任务队列为空时,VM转弯将结束.

以下是我的问题:

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

就像是

  • pushToMacroTask( function )
  • pushToMicroTask( function )

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

我很好,但是这并没有给我们一个有意义的API,你不觉得吗?

这个概念是否应该保留给JS开发者"隐藏"并仅用于某些hacky情况?

你知道围绕这个主题是否有任何W3C规范?

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

我很乐意听到有关这方面的故事和意见.

谢谢 !

tri*_*cot 7

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

W3C谈到任务队列:

当用户代理要对任务进行排队时,它必须将给定任务添加到相关事件循环的任务队列之一.来自一个特定任务源的所有任务(例如,计时器生成的回调,为鼠标移动调度的事件,排队等待解析器的任务)必须始终添加到同一任务队列,但可以将来自不同任务源的任务放入不同的任务队列.

EcmaScript2015讲述了作业队列,并要求至少支持两种:

  • ScriptJobs:验证和评估ECMAScript脚本和模块源文本的作业.
  • PromiseJobs:回应Promise的工作.

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

或者,[实现]可能会选择等待某个特定于实现的代理或机制来排队新的PendingJob请求.

EcmaScript规范不强制优先处理不同的作业队列:

此规范未定义服务多个作业队列的顺序.ECMAScript实现可以将作业队列的PendingJob记录的FIFO评估与一个或多个其他作业队列的PendingJob记录的评估交织在一起.

所以,这里似乎没有严格的要求,承诺履行应该在setTimeout任务之前得到服务.但是,当覆盖事件循环时,Web超文本应用技术工作组更具体 :

每个事件循环都有一个微任务队列.微任务是最初要在微任务队列而不是任务队列上排队的任务.

所有VM引擎都以同样的方式实现吗?

文章可能是一个有趣的阅读,显示浏览器的实现方式有什么不同导致不同的执行顺序:

一些浏览器之后正在运行promise回调setTimeout.很可能他们将承诺回调称为新任务的一部分而不是微任务.

FirefoxSafari正在耗尽点击侦听器之间的微任务队列,如变异回调所示,但承诺似乎排队不同.[...]有了Edge,我们已经看到它错误地排队了,但它也无法耗尽点击侦听器之间的微任务队列,而是在调用所有侦听器之后这样做.

本文写于2015年8月,从那时起,几个问题得到了解决和协调.

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

一个有意义的API

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

let pushToMicroTask = f => Promise.resolve().then(f);
let pushToMacroTask = f => setTimeout(f);
let say = msg => console.log(msg);

pushToMacroTask(say.bind(null, 'Macro task runs last'));
pushToMicroTask(say.bind(null, 'Micro task runs first'));
Run Code Online (Sandbox Code Playgroud)