Promise 的状态吸收
状态吸收指的是两个 promise ,其中一个去吸收另一个的状态,当一个 promise 状态改变时,另一个 promise 也会跟着改变。
可能发生的场景
状态吸收可能发生在多个场景:
// 一个完成的 promise
const promise1 = Promise.resolve('success');
// 一个拒绝的 promise
const promiseReject = Promise.reject('error');2
3
4
1. 构造函数中的状态吸收
// 新new一个 promise2 ,resolve的是一个 promise1,这时候 promise2 会吸收 promise1 的状态
const promise2 = new Promise((resolve, reject) => {
resolve(promise1);
});2
3
4
2. then 链式调用中的状态吸收
// then 方法返回的 promise3 会吸收 then 方法中的 promise1 状态
const promise3 = promise1.then((value) => promise1);2
3. async 函数中的状态吸收
// async 函数返回的 promise4 会吸收 async 函数中的 promise1 状态
async function test() {
return promise1;
}
const promise4 = test();2
3
4
5
4. Promise.race 和 Promise.all 中的状态吸收
// promise5 会吸收最先完成的 promise 的状态
const promise5 = Promise.race([promise1, new Promise((resolve) => setTimeout(resolve, 100))]);
// promise6 会吸收所有 promise 的状态,当所有都完成时才会 fulfilled
const promise6 = Promise.all([promise1, Promise.resolve('another')]);2
3
4
5
5. Promise.resolve 和 Promise.reject 中的状态吸收
// 直接吸收 promise1 的状态
const promise7 = Promise.resolve(promise1);
// 吸收 promiseReject 的拒绝状态
const promise8 = Promise.reject(promiseReject);2
3
4
5
6. catch 方法中的状态吸收
// 返回一个新的 promise,promise9 会吸收这个 promise 的状态
const promise9 = promiseReject.catch((reason) => {
return promise1;
});2
3
4
7. finally 方法中的状态吸收
// 返回一个新的 promise,promise10 会吸收这个 promise 的状态
const promise10 = promise1.finally(() => {
return promiseReject;
});2
3
4
8. Promise 链中的状态吸收
// promise11 会吸收链中最后一个返回的 promise 的状态
const promise11 = promise1
.then((value) => {
console.log(value); // 'success'
return Promise.resolve('chain1'); // 返回一个新的 promise
})
.then((value) => {
console.log(value); // 'chain1'
return Promise.reject('chainError'); // 返回一个拒绝的 promise
})
.catch((reason) => {
console.log(reason); // 'chainError'
return Promise.resolve('recovered'); // 返回一个新的 promise
});2
3
4
5
6
7
8
9
10
11
12
13
14
9. Promise.any 和 Promise.allSettled 中的状态吸收
// promise12 会吸收最先 fulfilled 的 promise 的状态
const promise12 = Promise.any([promiseReject, Promise.reject('another'), promise1]);
// promise13 会吸收所有 promise 的状态,无论 fulfilled 还是 rejected
const promise13 = Promise.allSettled([promise1, promiseReject]);2
3
4
5
10. 嵌套 Promise 中的状态吸收
// promise14 会吸收内部 promise1 的状态
function nestedPromise() {
return new Promise((resolve) => {
setTimeout(() => {
resolve(promise1); // 嵌套返回另一个 promise
}, 100);
});
}
const promise14 = nestedPromise();2
3
4
5
6
7
8
9
状态吸收的过程
V8引擎的状态吸收机制是一个精心设计的过程,涉及微任务队列的精确调度。当需要吸收一个Promise的状态时,V8引擎会创建特定的微任务并按顺序放入微队列中执行,主要分为以下两个关键步骤:
1. 准备阶段
在准备阶段,V8引擎会进行以下操作:
识别
Promise依赖关系:引擎会识别出哪些Promise对象之间存在状态依赖关系,即一个Promise的状态需要跟随另一个Promise的状态变化。这是通过检查resolve参数是否为thenable对象(具有then方法的对象)来实现的。创建
PromiseResolveThenableJob微任务:当检测到需要吸收状态时,V8引擎会创建第一个微任务——PromiseResolveThenableJob。这个任务负责处理thenable对象的状态吸收逻辑,确保状态能够正确地从源Promise传递到目标Promise。建立
状态观察者关系:V8引擎会在内部建立观察者模式,需要吸收状态的Promise(例如promise2)注册为被观察Promise(例如promise1)的状态观察者。这是通过PromiseReaction机制实现的,每个Promise都有一个内部属性[[PromiseReactions]],用于存储所有依赖它的Promise的反应。保存
执行上下文:引擎会保存当前执行上下文,包括需要执行的回调函数和相关的Promise引用,确保在微任务执行时能够正确恢复执行环境。
// 示例代码展示准备阶段
const promise1 = Promise.resolve('success');
const promise2 = new Promise((resolve, reject) => {
// 当resolve(promise1)被调用时,V8引擎进入准备阶段
// 1. 识别promise2依赖于promise1
// 2. 将promise2设置为promise1的状态观察者
// 3. 由于promise1已经是fulfilled状态,保存resolve回调
resolve(promise1);
});2
3
4
5
6
7
8
9
2. 吸收阶段
在吸收阶段,V8引擎会按特定顺序执行微任务队列中的任务:
执行
PromiseResolveThenableJob:第一个微任务PromiseResolveThenableJob会被执行,它会调用源Promise的then方法,并传入两个回调函数:一个用于处理fulfilled状态,另一个用于处理rejected状态。这些回调函数负责将源Promise的状态和值传递给目标Promise。创建
PromiseReactionJob:当源Promise的状态改变时,V8引擎会创建第二个微任务PromiseReactionJob。这个任务负责遍历源Promise的[[PromiseReactions]]列表,执行所有注册的反应,包括状态吸收逻辑。状态传递与锁定:在
PromiseReactionJob执行过程中,源Promise的状态和值会被传递给目标Promise。一旦传递完成,目标Promise的状态将被锁定为与源Promise相同的状态,并且这个状态不可逆转。即使源Promise后续状态再次改变,也不会影响已经完成状态吸收的目标Promise。微任务队列清空:
V8引擎会持续执行微任务队列中的所有任务,直到队列为空。这确保了所有相关的状态吸收操作都能在当前事件循环的微任务阶段完成,保证了Promise状态的一致性。
// 示例代码展示吸收阶段
const promise1 = Promise.resolve('success');
const promise2 = new Promise((resolve, reject) => {
resolve(promise1); // 准备阶段
});
// 吸收阶段:
// 1. promise1的fulfilled状态传递给promise2
// 2. 执行promise2的resolve回调,传入promise1的值'success'
// 3. promise2的状态被锁定为fulfilled,值为'success'
promise2.then((value) => {
console.log(value); // 输出: 'success'
});2
3
4
5
6
7
8
9
10
11
12
13
14
状态吸收的内部机制
在V8引擎内部,状态吸收是通过以下精密的机制实现的:
PromiseThenableJob:当resolve一个Promise对象时,V8会创建一个PromiseThenableJob微任务,这个任务负责处理状态吸收的核心逻辑。它会调用源Promise的then方法,并传入两个回调函数:一个用于处理fulfilled状态,另一个用于处理rejected状态。这些回调函数会创建PromiseReaction对象,并将其添加到源Promise的[[PromiseReactions]]列表中。PromiseReaction:每个Promise都有一个内部属性[[PromiseReactions]],这是一个列表,存储了所有依赖它的Promise的反应(reactions)。每个PromiseReaction对象包含了目标Promise的引用、处理fulfilled状态的回调函数和处理rejected状态的回调函数。当源Promise状态改变时,V8引擎会遍历这个列表,为每个PromiseReaction创建一个PromiseReactionJob微任务。PromiseResolveThenableJob:这是一个特殊的微任务,专门用于处理thenable对象(包括Promise)的状态吸收。它的工作流程是:- 调用
thenable对象的then方法 - 传入两个
回调函数,分别处理fulfilled和rejected状态 - 这些
回调函数会确保目标Promise的状态与thenable对象的状态保持同步 - 如果
thenable对象已经是fulfilled或rejected状态,会立即创建PromiseReactionJob微任务
- 调用
这三个机制协同工作,确保Promise状态能够正确地在不同的Promise对象之间传递和同步,同时保证了异步操作的顺序性和一致性。
// 内部实现模拟
function PromiseResolveThenableJob(promiseToResolve, thenable, then) {
// 创建PromiseReaction对象,包含目标Promise的引用和回调函数
const reaction = {
promise: promiseToResolve,
fulfillCallback: (value) => {
// 当thenable fulfilled时,promiseToResolve也fulfilled
PromiseResolve(promiseToResolve, value);
},
rejectCallback: (reason) => {
// 当thenable rejected时,promiseToResolve也rejected
PromiseReject(promiseToResolve, reason);
}
};
// 将reaction添加到thenable的[[PromiseReactions]]列表中
if (!thenable[[PromiseReactions]]) {
thenable[[PromiseReactions]] = [];
}
thenable[[PromiseReactions]].push(reaction);
// 调用thenable的then方法,传入处理fulfilled和rejected的回调函数
try {
then.call(
thenable,
(value) => {
// 创建PromiseReactionJob微任务,处理fulfilled状态
enqueuePromiseReactionJob(reaction, 'fulfill', value);
},
(reason) => {
// 创建PromiseReactionJob微任务,处理rejected状态
enqueuePromiseReactionJob(reaction, 'reject', reason);
}
);
} catch (error) {
// 如果调用then方法时抛出异常,直接reject目标Promise
PromiseReject(promiseToResolve, error);
}
}
// 执行PromiseReactionJob的函数
function PromiseReactionJob(reaction, type, value) {
if (type === 'fulfill') {
// 执行fulfill回调,传递value
reaction.fulfillCallback(value);
} else {
// 执行reject回调,传递reason
reaction.rejectCallback(value);
}
}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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
注意事项
- 循环依赖:如果两个
Promise互相吸收对方的状态,会导致无限循环,V8引擎会检测这种情况并抛出错误。
const promise1 = new Promise((resolve) => {
// 循环依赖,会抛出错误
resolve(promise2);
});
const promise2 = new Promise((resolve) => {
// 循环依赖,会抛出错误
resolve(promise1);
});2
3
4
5
6
7
8
9
性能考虑:
状态吸收涉及微任务的创建和执行,过多的Promise链式调用可能会影响性能。调试困难:由于
状态吸收是异步的,且涉及内部机制,在调试时可能会增加复杂度。
通过状态吸收机制,Promise能够实现复杂的状态传递和依赖关系管理,这是Promise链式调用和异步编程的重要基础。

