结论

  1. microtask macrotask 的顺序依赖于 host 实现,我们不应该去依赖其顺序来实现具体逻辑
  2. 讨论一个基于 host 的顺序问题犹如孔乙己讨论茴香的茴字有四种写法一样

背景

如果你面试前端,那你大概率对这个 issue 中的问题有所了解

这个问题不知从何时起成为了一个面试中常见的问题,各种文章解释也如雨后春笋出现,之前我也并没有深究过这个问题,但在群大佬的指导下我才发现:

这个问题,他或许并不合理

要回答这个问题,我们必须要抛开那些对顺序的讨论,回到一切的根本,最终的裁判,js 语言行为和表现的守望者,去探寻规范中对这种行为的真实定义

ECMA don’t care

首先需要表明的一个事实。

尽管这个问题常常被归类为 js 问题,其实不然。

如果我们仔细看看,这个问题中最核心的内容:

  1. Promise.then 回调的 microtask 是什么
  2. settimeoutmacrotask 是什么
  3. 以及 event loop 对两者的调度,(顺序如何

然而如果我们去查找 ECMA,就会发现

  1. ECMA 并没有对 event loop 做定义,也不考虑 event loop, event loop 实际上是 ECMA 的 host (例如 html or node)所定义和调度
  2. setTimeout 的属于 html 的 web api Timer 实现,同样不属于 ECMA 的规范范畴
  3. Promise.then 确实由 ECMA 所定义,但是
    1. 在 ECMA 中没有 microtask, 只有 job ,且 ECMA 明确指出 job 是一个抽象概念,其具体实现由 host 决定
    2. 目前规范中有且仅有 promise then 回调需要依赖 Job 实现,HostEnqueuePromiseJob
    3. microtask 实际上同样是出现于 html spec 中,用来在 html 环境下的作为 HostEnqueuePromiseJob 的 host 实现规范
    4. 只要能满足对 Jobs 约束的实现,ECMA 并不在意 host 使用 microtask 还是 macrotask,因为他对此无感知

可以说几乎这个顺序问题归属的绝大多数细节 ECMA 都不感兴趣(不由它所定义)

Host is matter

因此,这个顺序问题的所有细节都交由 host 去定义去实现,在不同的的 host (如 node 、html)下不一样的行为,都可以被接受

而现实中 node.js 10 之前和 html 的表现就不一样,当然最终 nodejs 为了兼容 web 端的表现,在 node 11 之后修改了行为。

但那又是另一回事了。

结语

  • 或许在有些 host 里并没有 microtask,setTimeout promise.then 同归一类
  • 或许在有些 host 里 microtask 不值一提,setTimeout 优先级远高于它
  • 或者在量子计算机 host 面前,两者天生就是并行触发,无所谓先后

在这个顺序问题中,不可知论者占领了上风