async函数中为什么如下两种await方式得出结果顺序不同?

新手上路,请多包涵

async函数中为什么如下两种await方式得出结果顺序不同?

function getResult(value,time) {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(value)
            resolve()
        }, time)
    })
}

(async () => {
    await getResult(1,300)
    await getResult(2,200)
})()

(async () => {
    const a = getResult(3,300)
    const b = getResult(4,200)
    await a
    await b
})()

又看了一遍async await原理还是没明白,求大佬解答

阅读 1.7k
5 个回答

从后往前看吧,先看最后一个执行,在 EventLoop

  • 会优先执行当前宏任务,结束后再执行宏任务中的微任务
  • 之后再执行下一个宏任务,依次类推

现在来看最后一段代码:

(async () => {
    const a = getResult(3,300)
    const b = getResult(4,200)
    await a
    await b
})()
  • 当前的宏任务是函数 getResult:执行 2 次
  • 在函数中返回并添加微任务 Promise 到队列中,添加了 2 个
  • 第一个 await 展开当前宏中的 Promise 微任务队列作为同步顺序执行
  • 从第一个 Promise 中提取 setTimeout 宏任务加入队列,300 毫秒后执行
  • 从第二个 Promise 中提取 setTimeout 宏任务加入队列,200 毫秒后执行
  • 当前宏任务中微任务执行完毕,等待执行下一个宏任务
  • 200 毫秒的宏任务优先执行输出 4,并释放第二个 await
  • 由于第一个 await 还未 resolve,并且当前宏任务中没有微任务继续往下执行
  • 300 毫秒的宏任务开始执行输出 3,并释放第一个 await
  • 第二个 await 开始执行,当前宏任务中已没有需要展开的微任务队列,并且已 resolve
  • 继续往下执行,由于上下文中既没有微任务也没有宏任务,结束执行

以上流程遵循一条原则,即:

  • await 会展开当前所有已队列的异步微任务作为同步函数顺序执行

因此,再来看最后这段代码:

(async () => {
    const a = getResult(3,300)    // 添加微任务到队列
    const b = getResult(4,200)    // 添加微任务到队列
    await a    // 执行所有提交到当前 `promise` 队列,展开作为同步执行
    await b    // 再次执行,当前宏任务中已没有微任务队列
})()

await a 时,已将微任务全部添加到队列


现在来看第一段有什么不一样:

  • 只有当微任务 resolvereject 后,才会完成整个 await
(async () => {
    await getResult(1,300)
    await getResult(2,200)
})()
  • 先执行当前宏任务函数 getResult,添加一个 promise 微任务队列
  • await 展开当前宏任务中,微任务队列作为上下文同步执行,这里队列只有 1 个
  • 执行 promise 微任务并添加一个 setTimeout 宏任务
  • 当前宏任务中微任务执行完毕,由于还未 resolve,继续执行下一个宏任务
  • setTimeout 宏任务中输出 1,并释放第一个 await

以上步骤中第二个 promise 微任务还没有添加到队列,只有当第一个 await 结束之后,才开始订阅下一个微任务,执行过程和上面一致


因此,第一段和第二段执行顺序的不同,是因为:

  • await 执行时,添加到当前宏任务中的 Promise 队列不同决定的

这段代码是

  1. 创建好getResult(1,300)里的promisesetTimeout后,
  2. getResult(1,300)里的setTimeout执行完成,
  3. 再执行await getResult(2,200)
(async () => {
    await getResult(1,300)
    await getResult(2,200)
})()

这段代码是

  1. 创建好getResult(3,300)里的promisesetTimeout
  2. 创建好getResult(4,200)里的promisesetTimeout
  3. 等待getResult(3,300)setTimeout执行完成
  4. 等待getResult(4,200)里的setTimeout执行完成
(async () => {
    const a = getResult(3,300)
    const b = getResult(4,200)
    await a
    await b
})()

哥们。你那是执行打印结果 不是promise的结果

同步执行后返回的promise才是关键 而不是内部打印结果

function getResult(value : number,time: number) {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(value)
            resolve(value)
        }, time)
    })
}

 async function test(){
    const a = getResult(3,300)
    const b = getResult(4,200)
    const r1 = await a
    const r2 = await b
    console.log(r1,r2)
}

test();

第一种方式:顺序执行

(async () => {
    await getResult(1, 300);
    await getResult(2, 200);
})();

步骤

  1. 调用 getResult(1, 300),开始计时300毫秒。
  2. await getResult(1, 300) 会等待 getResult(1, 300) 完成。
  3. 300毫秒后,getResult(1, 300) 完成,输出 1。
  4. 调用 getResult(2, 200),开始计时200毫秒。
  5. await getResult(2, 200) 会等待 getResult(2, 200) 完成。
  6. 200毫秒后,getResult(2, 200) 完成,输出 2。

输出顺序
1(等待300毫秒)
2(再等待200毫秒)

第二种方式:并行执行

(async () => {
    const a = getResult(3, 300);
    const b = getResult(4, 200);
    await a;
    await b;
})();

步骤

  1. 调用 getResult(3, 300),开始计时300毫秒。
  2. 调用 getResult(4, 200),开始计时200毫秒。
  3. await a 会等待 getResult(3, 300) 完成。
  4. await b 会等待 getResult(4, 200) 完成。

由于 getResult(4, 200) 的计时器时间较短(200毫秒),它会比 getResult(3, 300)(300毫秒)先完成。

输出顺序
4(等待200毫秒)
3(等待300毫秒)

setTimeout 的含义是在任务队列中添加一个任务,这个任务将在指定的时间(毫秒)之后执行

第一种 await 中:

await getResult(1, 300)
await getResult(2, 200)

由于你 await 了 getResult,所以第一个 getResult 执行完才会执行第二个 getResult,也就是说 300ms 之后才会添加一个 200ms 之后执行的任务;

而第二种 await 中:

const a = getResult(3, 300)
const b = getResult(4, 200)
await a
await b

由于你没有 await getResult,所以第一个 getResult 执行完之后将立即执行第二个 getResult,也就是说添加了一个 300ms 之后执行的任务之后又添加了一个 200ms 之后执行的任务,而 200ms 之后将执行第二个任务输出 4,100ms 之后第一个任务也将被执行输出 3

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏