🌞

为什么async/await 在chrome 环境和 node 环境的 执行结果不一致

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
async function async1() {
  console.log("a");
  await async2();
  console.log("b");
}
async function async2() {
  console.log('c');
}
async1();
new Promise(function (resolve) {
  console.log("d");
  resolve();
}).then(function () {
  console.log("e");
});

在Chrome中输出 👉 a -> c -> d -> b -> e

在Node中输入 👉 a -> c -> d -> e -> b

输出结果不同,我们可能下意识会想到,可能是V8引擎的版本不一样,的确是这样,那么,到底是哪里出了问题呢?

OK那我们现在先抛开这段代码,先回忆一下我们如何定义一个resolve(fulfill)状态的promise实例, 可能有如下两种方式吧:

  1. Promise.resolve();

  2. new Promise((resolve, reject) => resolve());

第二种方式可以给它起一个名字 RESOLVE()。 「其实来源于文档中对这种方式的称呼」

我们自然也可以给这两种方式传参,但是当我们给这种方式传入同样的参数却会发现有不一样的效果! 为什么呢?我们先来把参数分一下类吧,这里我们按照是否是thenable对象来分「thenable对象可以简单地理解为拥有then方法的对象」。

1.当参数为nonThenable对象时,RESOLVE(nonThenable)Promise.resolve(nonThenable)效果完全一样!

2.当参数为thenable对象时,效果就不一定了,为什么?是谁的锅🤦🏻‍♂️呢?对,就是promisePromise.resolve()对待thenable参数时还会根据是否是promise来区别对待....

  • 如果是Promise.resolve(promise)那么我们的引擎会直接立刻马上返回一个promise,意思就是:

    1
    2
    
    const p1 = xxxxx; //一个随便的promise
    Promise.resolve(p1) === p1; // true
    
  • 如果是Promise.resolve(nonPromiseThenable),并不会像上面直接返回一个promise,它可以等效成:

    1
    2
    3
    4
    5
    
    new Promise(resolve => { 
      Promise.resolve().then(() => { 
        nonPromiseThenable.then(resolve) 
      }) 
    });
    

而我们的RESOLVE才不像Promise.resolve()这么事逼,它对待thenable对象一视同仁,都会被转化成像Promise.resolve(nonPromiseThenable)那样:

1
2
3
4
5
new Promise(resolve => { 
      Promise.resolve().then(() => { 
        thenable.then(resolve) 
      }) 
});

而我们今天要讨论的重心其实promise,所以我们根据上面的现象得出结果 🤜🏻 RESOLVE(promise)Promise.resolve(promise)效果不同!我们也可以拿一段代码验证一下。

Promise.resolve()方式

1
2
3
4
5
6
7
let p1 = Promise.resolve(1)
Promise.resolve(p1).then(res => {
  console.log(res)
})
p1.then(res => {
  console.log(2)
})

会在浏览器中输出 1 -> 2

RESOLVE()方式

1
2
3
4
5
6
7
8
9
let p1 = Promise.resolve(1)
new Promise((resolve, reject) => {
  resolve(p1)
}).then(res => {
  console.log(res)
})
p1.then(res => {
  console.log(2)
})

会在浏览器中输出 2 -> 1

结果不同也就验证了刚才的结论了,但是此处特别想推导一下RESOLVE()为什么会输出2 -> 1。 因为其实resolve(p1)我们并不知道会怎么样,而我们在前面也说了,RESOLVE有一种等效形式,所以。 我们可以把代码变成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let p1 = Promise.resolve(1)
new Promise((resolve, reject) => {
  Promise.resolve().then(() => { 
    p1.then(resolve) 
  }) 
}).then(res => {
  console.log(res)
})
p1.then(res => {
  console.log(2)
})

OK,现在的代码我们可以来分析了!但是在分析前我们还要掌握一个知识点就是 -> then方法接受的回调是何时被放入 microTasks「浏览器中的叫法,在ES中也叫Job Queue」的? 如果当调用 then 时 promise 是 pending 状态,回调会进入 promise 的 [[PromiseFulfill/RejectReactions]] 列表里;否则会进入 PromiseJobs。当该 promise 被 resolve 或 reject 时,则 flush 对应的 reactions 队列 ,其中的每个 reaction 对应一个 PromiseJob 被按序 enqueue 到 Job Queue如果调用 then 时 promise 处于其他两个状态,JS 引擎就直接 enqueue 一个对应的 PromiseJob 到 Job Queue示例中的代码。

流程图◎ 流程图

到这里以后,可能会有人问,为什么要研究RESOLVE()Promise.resolve()的区别呢? 其实是因为async函数里的await x语法就可以等效成 RESOLVE(x)或者Promise.resolve(x),所以结果自然不同。async的实现会在下次分享。。。

updatedupdated0001-01-010001-01-01