JavaScript 结合 promise.then 和异步函数的打印序列测验

JavaScript quiz of printing sequence with combination of promise.then and async function

我对使用异步函数执行 promise.then 的顺序感到困惑。控制台上显示的测验结果是:1, a, 2, b, c, d, 3, e.

我在想:

这是我对这些假设的测试:

https://codepen.io/allieschen/pen/WNMjgPx

以下是原题答题码:

new Promise((resolve) => {
  console.log(1);
  resolve();
  })
  .then(async () => {
    console.log(2);
  })
  .then(async () => {
    console.log(3);
  });

new Promise((resolve) => {
  console.log("a");
  resolve();
  })
  .then(() => {
    console.log("b");
  })
  .then(() => {
    console.log("c");
  })
  .then(() => {
    console.log("d");
  })
  .then(() => {
    console.log("e");
  });

我已经将调试器与 Node.js 一起使用,它表明从 b 到 e,它们不断遍历(被解析?)堆栈。然后程序会返回去完成aync函数并打印2.

所以,我希望看到:1, a, 2, b, c, d, e, 3

非常感谢您对这个问题的任何帮助。

为了回答你的问题,让我先试着解释一下你问题中的代码是如何执行的。

  1. 第一个 promise 构造函数被执行

    new Promise((resolve) => {
      console.log(1);
      resolve();
    })
    

    在控制台上记录 1 并同步解析 promise

  2. 随着 promise 的解决,一个作业在 micro-task 队列中排队,以执行传递给 then() 方法的实现处理程序

    new Promise((resolve) => {
      ...
    })
    .then(async () => {
      console.log(2);
    })
    
    micro-task queue: [ job(console.log(2)) ]
    
    console output: 1
    
  3. 执行第二个 promise 构造函数

    new Promise((resolve) => {
      console.log("a");
      resolve();
    })
    

    在控制台上记录 'a' 并同步解析 promise

  4. 随着 promise 的解决,一个作业在 micro-task 队列中排队,以执行传递给 then() 方法的实现处理程序

    new Promise((resolve) => {
      ...
    })
    .then(async () => {
      console.log("b");
    })
    
    micro-task queue: [ job(console.log(2)), job(console.log('b')) ]
    
    console output: 1 a
    
  5. 脚本同步执行结束后,javascript开始处理micro-task队列

    micro-task queue: [ job(console.log(2)), job(console.log('b')) ]
    
    console output: 1 a
    
  6. 来自 micro-task 的第一个作业已出列并处理,在控制台上记录 2

    micro-task queue: [ job(console.log('b')) ]
    
    console output: 1 a 2
    
  7. 由于传递给第一个承诺的第一个 then() 方法的履行处理程序是一个 async 函数,因此履行处理程序隐含地 return 是一个承诺。

    如果传递给then方法的回调函数的return值也是一个promise,则then()方法returned的promise是解决 由其回调函数 return 承诺。

    换句话说,外部承诺(return由 then() 方法编辑)的命运取决于内部承诺(return由其回调函数编辑)。如果内部承诺实现,外部承诺也将以相同的价值实现。

    以下代码示例演示了 then 方法 return 的承诺如何 解析 到回调 return 的承诺功能:

    const outerPromise = new Promise((resolveOuter, rejectOuter) => {
       const innerPromise = new Promise((resolveInner, rejectInner) => {
          resolveInner(); 
       });
    
       // resolve/reject functions of the outer promise are 
       // passed to the `then()` method of the inner promise 
       // as the fulfilment/rejection handlers respectively
       innerPromise.then(resolveOuter, rejectOuter);
    });
    

    解决外部承诺到内部承诺,另一个工作将在micro-task队列

    中排队
    micro-task queue: [ job(console.log('b')), job(resolve(outerPr, innerPr) ]
    
    console output: 1 a 2
    
  8. micro-task 队列中的下一个作业已出列并处理,在控制台上记录 'b'

    micro-task queue: [ job(resolve(outerProm, innerProm) ]
    
    console output: 1 a 2 b
    
  9. 作为在控制台上记录 'b' 的回调隐式 returned undefined,承诺 returned 由包装器 then() 方法实现值为 undefined.

    因此,另一个作业在 micro-task 队列中排队以执行下一个 then() 方法的实现处理程序

    new Promise((resolve) => {
      ...
    })
    .then(() => {
      ...
    })
    .then(() => {
      console.log("c");
    })
    
    micro-task queue: [job(resolve(outerProm, innerProm), job(console.log('c')]
    
    console output: 1 a 2 b
    
  10. micro-task 队列中的下一个作业已出列并处理。由于下一项工作与一个等待另一个承诺解决(解决或拒绝)的承诺相关,因此内部承诺用 undefined 的值解决(值 return 由相应的回调函数编辑)(参见步骤 7)。

    因此,另一个作业在 micro-task 队列中排队以执行内部承诺的履行处理程序。在这种情况下,履行处理程序是外部承诺的 resolve 功能。调用它会解析外部 promise

    micro-task queue: [ job(console.log('c'), job(resolve(outerProm), ]
    
    console output: 1 a 2 b
    
  11. micro-task 队列中的下一个作业已出列并处理,在控制台上记录 'c'

    micro-task queue: [ job(resolve(outerProm), ]
    
    console output: 1 a 2 b c
    
  12. 作为在控制台上记录 'c' 的回调隐式 returned undefined,承诺 returned 由包装器 then() 方法实现值为 undefined.

    因此,另一个作业在 micro-task 队列中排队以执行下一个 then() 方法的实现处理程序

    new Promise((resolve) => {
      ...
    })
    .then(() => {
      ...
    })
    .then(() => {
      ...
    })
    .then(() => {
      console.log("d");
    })
    
    micro-task queue: [ job(resolve(outerProm), job(console.log('d') ]
    
    console output: 1 a 2 b c
    
  13. micro-task 队列中的下一个作业被出列并处理,解决由第一个承诺 then() 方法编辑的承诺 return

    new Promise((resolve) => {
      ...
    })
    .then(async () => {
      console.log(2);
    })
    

    因此,作业在 micro-task 队列中排队以执行其履行处理程序

    micro-task queue: [ job(console.log('d'), job(console.log(3) ]
    
    console output: 1 a 2 b c
    
  14. micro-task 队列中的下一个作业已出列并处理,在控制台上记录 'd'

    micro-task queue: [ job(console.log(3) ]
    
    console output: 1 a 2 b c d
    
  15. 在控制台上记录 'd' 的回调执行后,另一个作业在 micro-task 队列中排队以在控制台上记录 'e'

    micro-task queue: [ job(console.log(3), job(console.log('e') ]
    
    console output: 1 a 2 b c d 
    
  16. 最后两个job会依次出队处理,最终输出为:

    micro-task queue: [  ]
    
    console output: 1 a 2 b c d 3 e
    

现在回答您的问题:

Why 3 just inserts to between d and e?

详见上文

Also, I found that fulfilled with async function would insert to the task queue every three promise.then, but why?

如果你理解了上面的解释,你现在也理解了代码笔中的输出。

Does fulfilled with async function act just like return a Promise.resolve?

是的。由于 async 函数隐式 return 是一个承诺,returning Promise.resolve()then() 方法的回调函数将具有相同的效果(参见上面的步骤 7) .


注意:虽然了解代码的输出对于深入理解语言是一件好事,但real-world代码不应该依赖于两个承诺的时间不同的不相关的承诺链。