代码之家  ›  专栏  ›  技术社区  ›  byles1506

不理解异步等待执行顺序

  •  2
  • byles1506  · 技术社区  · 1 年前
    async function funcTwo() {
      return new Promise((r) => r());
    }
    
    async function funcOne() {
      console.log("A");
    
      (async () => {
        await funcTwo();
        console.log("B");
      })();
      console.log("C");
    }
    
    await funcOne();
    
    console.log("Done");
    

    据我所知,输出应如下:

    A
    C
    B 
    Done
    

    我的推理是:

    1. funcOne

    2. A 已打印

    3.async函数运行后,promise立即被解析 console.log("B") 被移至镜像任务队列

    4. C 已打印

    5.funcOne已解决 console.log("Done"); 被移至镜像任务队列。

    6.从队列中提取任务 B Done 按此顺序打印。( console.log(“B”) 之前已添加到队列中 console.log("done") )

    然而,输出是:

    A
    C
    Done
    B
    

    B 多恩 已切换

    有人能解释一下我做错了什么吗?

    2 回复  |  直到 1 年前
        1
  •  2
  •   Kaiido    1 年前

    如果你有 async 执行函数 return 有了promise对象,函数返回的promise将处于挂起状态,永远不会解决。承诺由 async 功能将是 已锁定 履行对 返回 声明。

    让我们为涉及的承诺命名。这基本上是相同的脚本,但所有相关promise都有变量名:

    async function f2() {
        const p0 = Promise.resolve();
        return p0;
    }
    
    async function f1() {
        console.log("A");
    
        async function f3() {
            const p2 = f2();
            await p2;
            console.log("B");
        }
        
        const p3 = f3();
        console.log("C");
    }
    
    const p1 = f1();
    const p4 = await p1;
    console.log("Done");
    

    尽管 p0 是一个兑现的承诺, p2 不是。 p2 已锁定到 p0 ,内部a then 附于 p0 为了实现它,所以 p2 可以遵循相同的状态转换,也可以实现。但这样(隐藏) 然后 回调需要一个promise作业放入队列(微任务队列)。因此, p0 不会同步完成,而是稍后完成。

    另一方面, funcOne 返回一个已实现的promise,因为没有 await 它自己执行。请注意 等待 在嵌套匿名函数中,仅暂停该匿名函数,而不是 funcOne 后者将继续记录“B”,而不考虑任何promise的状态,因此它返回 undefined 作为履行承诺的价值。

    如果你用这个原则进行推理,你会看到你得到的输出是预期的。

    这是操作顺序的简化视图。它描述了左侧的调用堆栈、正在执行的当前操作、所有相关promise的状态(F=已完成,P=待定)以及promise作业队列(微任务队列)中的作业:

    调用堆栈 行动 p0 p1 p2 p3 Promise作业队列
    脚本 f1()
    脚本>f1 log('A')
    脚本>f1 f3()
    脚本>f1>;f3 f2()
    脚本>f1>;f3>;f2 r()
    脚本>f1>;f3>;f2 p0 = resolve(...) F
    脚本>f1>;f3>;f2 return p0 F
    脚本>f1>;f3 p2 = ... F P fulfill(p2)
    脚本>f1>;f3 await p2 F P 履行(p2)
    脚本>f1 p3 = ... F P P 履行(p2)
    脚本>f1 log('C') F P P 履行(p2)
    脚本>f1 返回 F P P 履行(p2)
    脚本 p1 = ... F F P P 履行(p2)
    脚本 await p1 F F P P 履行(p2) resume script
    检查队列 F F P P 履行(p2) 简历脚本
    fullfill(p2) 满填充(p2) F F F P 简历脚本 resume f3
    检查队列 F F F P 简历脚本 简历f3
    脚本已恢复 log('D') F F F P 简历f3
    检查队列 F F F P 简历f3
    f3恢复 log('B') F F F P
    f3恢复 返回 F F F F
        2
  •  0
  •   Robert Harvey    1 年前

    这是一个棘手的练习,探讨了任务队列、微任务队列以及事件循环如何工作等概念。

    当程序运行时, await funcOne 将被调用(并放置在微任务队列中),它将立即输出“A”,因为这是一个同步操作。接下来,IIFE将执行。在 await funcTwo() line,funcTwo立即运行并完成。然而 .then promise的回调不会立即执行。相反,它被放置在微任务队列中。

    然后,程序将从IIFE继续前进,而无需等待(因为没有任何等待来保存它)其结果完成,并继续进行下一个同步任务,即记录“C”。此时,funcOne的执行完成,程序在 await funcOne(); ,记录“完成”。因为这是下一个同步操作。

    一旦所有同步任务都完成,事件循环将处理微任务队列。此时将记录“B”。

    在这个 link ,你可以对这三个概念有更详细的解释。