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
.
我在想:
- 为什么
3
只是插入到 d
和 e
之间?
- 另外,我发现 fulfilled with async function 会每三个
promise.then
插入任务队列,但为什么呢?
- 使用异步函数实现是否就像 return 和
Promise.resolve
一样?
这是我对这些假设的测试:
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
。
非常感谢您对这个问题的任何帮助。
为了回答你的问题,让我先试着解释一下你问题中的代码是如何执行的。
第一个 promise 构造函数被执行
new Promise((resolve) => {
console.log(1);
resolve();
})
在控制台上记录 1
并同步解析 promise
随着 promise 的解决,一个作业在 micro-task 队列中排队,以执行传递给 then()
方法的实现处理程序
new Promise((resolve) => {
...
})
.then(async () => {
console.log(2);
})
micro-task queue: [ job(console.log(2)) ]
console output: 1
执行第二个 promise 构造函数
new Promise((resolve) => {
console.log("a");
resolve();
})
在控制台上记录 'a'
并同步解析 promise
随着 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
脚本同步执行结束后,javascript开始处理micro-task队列
micro-task queue: [ job(console.log(2)), job(console.log('b')) ]
console output: 1 a
来自 micro-task 的第一个作业已出列并处理,在控制台上记录 2
micro-task queue: [ job(console.log('b')) ]
console output: 1 a 2
由于传递给第一个承诺的第一个 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
micro-task 队列中的下一个作业已出列并处理,在控制台上记录 'b'
micro-task queue: [ job(resolve(outerProm, innerProm) ]
console output: 1 a 2 b
作为在控制台上记录 '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
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
micro-task 队列中的下一个作业已出列并处理,在控制台上记录 'c'
micro-task queue: [ job(resolve(outerProm), ]
console output: 1 a 2 b c
作为在控制台上记录 '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
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
micro-task 队列中的下一个作业已出列并处理,在控制台上记录 'd'
micro-task queue: [ job(console.log(3) ]
console output: 1 a 2 b c d
在控制台上记录 'd'
的回调执行后,另一个作业在 micro-task 队列中排队以在控制台上记录 'e'
micro-task queue: [ job(console.log(3), job(console.log('e') ]
console output: 1 a 2 b c d
最后两个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代码不应该依赖于两个承诺的时间不同的不相关的承诺链。
我对使用异步函数执行 promise.then
的顺序感到困惑。控制台上显示的测验结果是:1, a, 2, b, c, d, 3, e
.
我在想:
- 为什么
3
只是插入到d
和e
之间? - 另外,我发现 fulfilled with async function 会每三个
promise.then
插入任务队列,但为什么呢? - 使用异步函数实现是否就像 return 和
Promise.resolve
一样?
这是我对这些假设的测试:
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
。
非常感谢您对这个问题的任何帮助。
为了回答你的问题,让我先试着解释一下你问题中的代码是如何执行的。
第一个 promise 构造函数被执行
new Promise((resolve) => { console.log(1); resolve(); })
在控制台上记录
1
并同步解析 promise随着 promise 的解决,一个作业在 micro-task 队列中排队,以执行传递给
then()
方法的实现处理程序new Promise((resolve) => { ... }) .then(async () => { console.log(2); })
micro-task queue: [ job(console.log(2)) ] console output: 1
执行第二个 promise 构造函数
new Promise((resolve) => { console.log("a"); resolve(); })
在控制台上记录
'a'
并同步解析 promise随着 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
脚本同步执行结束后,javascript开始处理micro-task队列
micro-task queue: [ job(console.log(2)), job(console.log('b')) ] console output: 1 a
来自 micro-task 的第一个作业已出列并处理,在控制台上记录
2
micro-task queue: [ job(console.log('b')) ] console output: 1 a 2
由于传递给第一个承诺的第一个
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
micro-task 队列中的下一个作业已出列并处理,在控制台上记录
'b'
micro-task queue: [ job(resolve(outerProm, innerProm) ] console output: 1 a 2 b
作为在控制台上记录
'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
micro-task 队列中的下一个作业已出列并处理。由于下一项工作与一个等待另一个承诺解决(解决或拒绝)的承诺相关,因此内部承诺用
undefined
的值解决(值 return 由相应的回调函数编辑)(参见步骤 7)。因此,另一个作业在 micro-task 队列中排队以执行内部承诺的履行处理程序。在这种情况下,履行处理程序是外部承诺的
resolve
功能。调用它会解析外部 promisemicro-task queue: [ job(console.log('c'), job(resolve(outerProm), ] console output: 1 a 2 b
micro-task 队列中的下一个作业已出列并处理,在控制台上记录 'c'
micro-task queue: [ job(resolve(outerProm), ] console output: 1 a 2 b c
作为在控制台上记录
'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
micro-task 队列中的下一个作业被出列并处理,解决由第一个承诺
then()
方法编辑的承诺 returnnew 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
micro-task 队列中的下一个作业已出列并处理,在控制台上记录
'd'
micro-task queue: [ job(console.log(3) ] console output: 1 a 2 b c d
在控制台上记录
'd'
的回调执行后,另一个作业在 micro-task 队列中排队以在控制台上记录'e'
micro-task queue: [ job(console.log(3), job(console.log('e') ] console output: 1 a 2 b c d
最后两个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代码不应该依赖于两个承诺的时间不同的不相关的承诺链。