const p1 = new Promise((_, reject) => {
reject(1);
}).then((r)=>console.log(r)).catch((r)=>console.log(r));
const p2 = new Promise((resolve, _) => {
resolve(10);
}).then((r)=>console.log(r)).catch((r)=>console.log(r));
// output -> 10, 1
输出不应该是 1, 10 吗?因为 p1 的 catch 块在 p2 的块之前添加到微任务队列中? 下面的示例给出了 1,10
的预期输出const p1 = new Promise((_, reject) => {
reject(1);
}).catch((r)=>console.log(r))
const p2 = new Promise((resolve, _) => {
resolve(10);
}).then((r)=>console.log(r)).catch((r)=>console.log(r));
为了说明微任务队列如何通过脚本的执行而演变,我首先建议对脚本进行一些更改,以便我们可以更轻松地识别所涉及的承诺和回调:
// Assign all used callbacks to distinct variables (for easy reference)
const p1a_then = console.log;
const p1b_catch = console.log;
const p2a_then = console.log;
const p2b_catch = console.log;
// Use available short-cuts for immediately settling promises
// and assign each created promise to a separate variable (for easy reference)
const p1a = Promise.reject(1);
const p1b = p1a.then(p1a_then);
const p1 = p1b.catch(p1b_catch);
const p2a = Promise.resolve(10);
const p2b = p2a.then(p2a_then);
const p2 = p2b.catch(p2b_catch);
请注意 Promise 链是如何拆分为多个语句/赋值的,但这不会影响链本身:它仍然表示相同的执行流程,但具有更多变量引用,我们可以在分析此流程时使用这些变量引用。
经过一些简化,我们可以像这样表示执行流程:
步骤 | 调用堆栈 | 行动 | p1a | p1b | p1 | p2a | p2b | p2 | 微任务队列 |
---|---|---|---|---|---|---|---|---|---|
1 | 脚本 |
|
R | ||||||
2 | 脚本 |
|
R | P |
|
||||
3 | 脚本 |
|
R | P | P |
|
|||
4 | 脚本 |
|
R | F | |||||
5 | 脚本 |
|
R | P | F | P | ,
|
||
6 | 脚本 |
|
R | P | P | F | P | P | ,
|
7 | - | 处理微任务队列 | R | P | P | F | P | P | ,
|
8 |
|
(通过)拒绝
|
R | R | P | F | P | P | 、
|
9 | - | 处理微任务队列 | R | R | P | F | P | P | 、
|
10 |
|
log(10),满足
|
R | R | P | F | F | P | ,
|
11 | - | 处理微任务队列 | R | R | P | F | F | P | 、
|
12 |
|
log(1),满足
|
R | R | F | F | F | P |
|
13 | - | 处理微任务队列 | R | R | F | F | F | P |
|
14 |
|
(通过)满足
|
R | R | F | F | F | F | |
15 | - | 处理微任务队列 | R | R | F | F | F | F |
对此表的一些评论:
最后一列列出了微任务队列中的条目,并带有“函数/承诺”符号:当该微任务条目被消耗时,该函数将被调用,其返回值将用于解析/拒绝斜杠后面提到的承诺.
该列有时会列出不存在的函数,例如
p1a_catch
。这是为了表明,虽然代码中没有提供这个处理程序,但仍然需要执行一个操作:默认操作(“传递”)将拒绝 Promise p1b
。
右侧附近的列给出了每个 Promise 的状态:[P]ending、[F]ulfilled、[R]ejected。
请注意,只有当回调所附加的 Promise 已解决时,回调才会在微任务队列中排队。例如,在步骤 3 中,回调
p1b_catch
尚未 添加到微任务队列中,因为 p1b
尚未解决。这可能令人惊讶,但为了解决
p1b
,我们首先需要执行
p1a_then
,因为这决定了
p1b
将如何解析,并且该执行只会异步发生,即稍后发生。一般来说,
then
/
catch
方法调用将always 返回一个 pending 承诺,无论您调用该
then
/
catch
方法的承诺状态如何。我希望这能澄清这一点。