承诺微任务队列中的执行顺序

问题描述 投票:0回答:1
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));
javascript es6-promise asynchronous-javascript
1个回答
0
投票

为了说明微任务队列如何通过脚本的执行而演变,我首先建议对脚本进行一些更改,以便我们可以更轻松地识别所涉及的承诺和回调:

// 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 脚本
p1a = Promise.reject(1)
R
2 脚本
p1b = p1a.then(p1a_then)
R P
p1a_catch/p1b
3 脚本
p1  = p1b.catch(p1b_catch)
R P P
p1a_catch/p1b
4 脚本
p2a = Promise.resolve(10)
R F
5 脚本
p2b = p2a.then(p2a_then)
R P F P
p1a_catch/p1b
p2a_then/p2b
6 脚本
p2  = p2b.catch(p2b_catch)
R P P F P P
p1a_catch/p1b
p2a_then/p2b
7 - 处理微任务队列 R P P F P P
p1a_catch/p1b
p2a_then/p2b
8
p1a_catch
(通过)拒绝
p1b
R R P F P P
p2a_then/p2b
p1b_catch/p1
9 - 处理微任务队列 R R P F P P
p2a_then/p2b
p1b_catch/p1
10
p2a_then
log(10),满足
p2b
R R P F F P
p1b_catch/p1
p2b_then/p2
11 - 处理微任务队列 R R P F F P
p1b_catch/p1
p2b_then/p2
12
p1b_catch
log(1),满足
p1
R R F F F P
p2b_then/p2
13 - 处理微任务队列 R R F F F P
p2b_then/p2
14
p2b_then
(通过)满足
p2
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
 方法的承诺状态如何。

我希望这能澄清这一点。

© www.soinside.com 2019 - 2024. All rights reserved.