async/await 在chrome 环境和 node 环境的 执行结果不一致,求解?

async function async1() { console.log("a"); await async2(); console.log("b"…
关注者
227
被浏览
81,487
登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏

【2018年9月更新】

根据 TC39 最近决议,await将直接使用Promise.resolve()相同语义。也就是说V8将重新恢复原有行为(也就是原题中的node的行为)。其他引擎也会照此修改。主要原因可能是性能考量。



【2018年3月原答案】


你观察到的这个差异应是V8最新版本与稍老版本的差异。与是浏览器还是Node.js没有关系,所以看那两个文档并没有什么用。你需要看的是ECMAScript规范。不过规范不是写给一般人看的,所以95%以上可能,你就算看了规范也看不明白哪一种是符合规范的行为。


一般来说,当遇到Chrome和Node.js在JavaScript运行方面有差异,应以最新的Chrome的行为为准。

虽然Chrome和Node.js都使用V8为其JavaScript引擎,但两者的V8更新策略不同。Chrome每次升级会同时更新到V8的最新版。而Node更新小版本时V8也只更新小版本,只有Node更新大版本时才会更新V8大版本。所以绝大部分时候Node的V8会比同时期的Chrome的V8要落后。

然后,比较老的V8(即目前Node使用的V8)对Promise和Async/Await的处理可能有少许不完全符合标准之处,你所观察到的差异就是一个。不过通常这并不影响使用。因为一般来说,你不应该过分依赖异步操作的顺序。


具体到这个case,关键点在于『await promise』的语义到底是什么。一般而言,我们可以把

async function f() {
  await p
  console.log('ok')
}

简化理解为:

function f() {
  return RESOLVE(p).then(() => {
    console.log('ok')
  })
}

『RESOLVE(p)』接近于『Promise.resolve(p)』,不过有微妙而重要的区别:p 如果本身已经是 Promise 实例,Promise.resolve 会直接返回 p 而不是产生一个新 promise。【个人认为 Promise.resolve 的这一『优化』行为可能是弊大于利的,不过因为已经写进标准了,也不太可能修改了。】老版本V8的问题是当 p 是一个已经 settled 的 promise,会进行类似的激进优化,导致执行时序与非 settled 的 promise 结果不同。比如把你的 async2 中加入一个 await 语句,老版本行为就和新版本一致了。

以上。


【怕大家看不懂,我再稍微补充一下。如果 RESOLVE(p) 对于 p 为 promise 直接返回 p 的话,那么 p 的 then 方法就会被马上调用,其回调就立即进入 job 队列。而如果 RESOLVE(p) 严格按照标准,应该是产生一个新的 promise,尽管该 promise 确定会 resolve 为 p,但这个过程本身是异步的,也就是现在进入 job 队列的是新 promise 的 resolve 过程,所以该 promise 的 then 不会被立即调用,而要等到当前 job 队列执行到前述 resolve 过程才会被调用,然后其回调(也就是继续 await 之后的语句)才加入 job 队列,所以时序上就晚了。】