图解一道刁钻的Promise题目

1.题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4);
}).then((res) => {
console.log(res)
})

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

//0
//1
//2
//3
//4
//5
//6

2.考察点

2.1 Promise 的微任务队列

  • Promise 的回调(then、catch、finally)会被放入微任务队列
  • 微任务队列在当前宏任务执行完毕后立即执行,直至清空微任务队列中的所有任务

2.2 事件循环(Event Loop)

  • JavaScript 是单线程的,通过事件循环机制处理异步任务
  • 事件循环分为宏任务(如 setTimeout、setInterval)和微任务(如 Promise)
  • 微任务的优先级高于宏任务。

2.3 Promise 的链式调用

  • then 方法返回一个新的 Promise,实现链式调用
  • 如果 then 的回调返回一个 Promise,后续的 then 会等待该 Promise 完成后再执行。

2.4 Promise.resolve 的作用

Promise.resolve(value) 这个方法是 ES6 中的Promise提供的一个静态方法, 返回一个 Promise 对象。返回Promise对象处于什么状态取决入传入的参数.

  • 如果这个值是一个 Promise,将返回一个 Promise
  • 如果这个值是一个 thenable,返回的 Promise 会使用这个thenable,并且取它的最终状态
  • 否则返回的 promise 将以此值完成,即用此值执行去 resolve()方法

简单实现一下 resolve 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Promise {
...
}

Promise.resolve = (v) => {
if (v instanceof Promise) {
return v;
} else if (v instanceof Object && "then" in v) {
return new Promise((resolve, reject) => {
v.then(resolve, reject);
});
}
return new Promise((resolve) => {
resolve(v);
});
};

3.解题方案

将提供2种解题方案

3.1执行过程分析

  • 同步代码执行 :

    • 两个 Promise.resolve() 会立即执行,分别将它们的 then 回调放入微任务队列。
  • 微任务队列的执行 :

    • 第一个微任务(第一个 Promise 链的第一个 then):

      • 打印 0

      • 返回 Promise.resolve(4),这会创建一个新的 Promise,并将其放入微任务队列。

    • 第二个微任务(第二个 Promise 链的第一个 then):

      • 打印 1

      • 返回 undefined(默认返回值),将下一个 then 回调放入微任务队列。

    • 第三个微任务(第二个 Promise 链的第二个 then):

      • 打印 2

      • 返回 undefined,将下一个 then 回调放入微任务队列。

    • 第四个微任务(第二个 Promise 链的第三个 then):

      • 打印 3

      • 返回 undefined,将下一个 then 回调放入微任务队列。

    • 第五个微任务(第一个 Promise 链的第二个 then):

      • 等待 Promise.resolve(4) 完成,打印 4
    • 第六个微任务(第二个 Promise 链的第四个 then):

      • 打印 5

      • 返回 undefined,将下一个 then 回调放入微任务队列。

    • 第七个微任务(第二个 Promise 链的第五个 then):

      • 打印 6

3.2任务队列图

输出 微任务队列
[0,1]
0 [1,then]
1 [then,2]
[2,resolve]
2 [resolve,3]
[3,4]
3 [4,5]
4 [5,6]
5 [6]
6 []

3.3转化代码

结合上边的resolve方法将第一段Promise修改一下

1
2
3
4
5
6
7
8
9
10
Promise.resolve().then(() => {
console.log(0);
new Promise((resolve) => {
resolve(4);
}).then((res) => {
return res;
})then((res) => {
console.log(res);
});
});

此时题目变成了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Promise.resolve().then(() => {
console.log(0);
new Promise((resolve) => {
resolve(4);
}).then((res) => {
return res;
}).then((res) => {
console.log(res);
});
});

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

看到这,我想大部分同学应该已经恍然大悟了,原来如此。