【JavaScript】promise是如何工作的
Promise 使用方式
给 resolve 传入非对象
new Promise((resolve, reject) => {
if (Math.random() > 0.5) {
resolve(true);
} else {
reject(false);
}
});
给 resolve 传一个对象
如果对象中有 then
属性且值为函数
new Promise((resolve) => {
console.log("promise");
resolve({
then: (res, rej) => {
if (true) {
res(true);
} else {
rej(false);
}
},
});
});
Promise 工作原理解析
内部属性
在 Promise
内部有一些属性,这些属性对外不可见,用于跟踪记录 Promise
的状态
[[PromiseState]]
表示Promise
的状态,包括:pending
,fulfilled
和rejected
,控制Promise
对其then
方法的传入调用做何反应[[PromiseResult]]
表示Promise
的状态是否转为已完成。只有在[[PromiseState]]
不是pending
时才有意义[[PromiseFulfillReactions]]
数组,调用 then 时如果 promise 的状态还是 padding 就把 resolve 处理程序添加到这个数组[[PromiseRejectReactions]]
数组,调用 then 时如果 promise 的状态还是 padding 就把 reject 处理程序添加到这个数组[[PromiseIsHandled]]
一个布尔值,表示Promise
是否曾经有一个履行或拒绝处理程序;用于未处理的拒绝跟踪。
export const PledgeSymbol = Object.freeze({
state: Symbol("PledgeState"),
result: Symbol("PledgeResult"),
isHandled: Symbol("PledgeIsHandled"),
fulfillReactions: Symbol("PledgeFulfillReactions"),
rejectReactions: Symbol("PledgeRejectReactions")
});
export class Pledge {
// ...
this[PledgeSymbol.state] = "pending";
this[PledgeSymbol.result] = undefined;
this[PledgeSymbol.isHandled] = false;
this[PledgeSymbol.fulfillReactions] = [];
this[PledgeSymbol.rejectReactions] = [];
// ...
}
reject() 函数
reject 函数接收一个参数,并将承诺改为 rejected 状态,reject 之后可以用 then 的第二个参数或 catch 捕获
export function rejectPledge(pledge, reason) {
if (pledge[PledgeSymbol.state] !== "pending") {
throw new Error("Pledge is already settled.");
}
const reactions = pledge[PledgeSymbol.rejectReactions];
pledge[PledgeSymbol.result] = reason;
pledge[PledgeSymbol.fulfillReactions] = undefined;
pledge[PledgeSymbol.rejectReactions] = undefined;
pledge[PledgeSymbol.state] = "rejected";
if (!pledge[PledgeSymbol.isHandled]) {
// TODO: perform HostPromiseRejectionTracker(promise, "reject").
}
// TODO: Return `TriggerPromiseReactions(reactions, reason)`.
}
resolve() 函数
resolve 函数接收一个参数,并将承诺改为 fulfilled 状态,resolve 之后可以用 then 的第一个参数
resolve 处理不同类型值的方式: 1. 参数为本身 promise 则抛出错误。 2. 参数为其他 promise 则等待 promise 完成,使用另一个承诺调用 resolve 意味着在第二个承诺被解决之前,第一个承诺不能被解决。 3. 参数为非对象则使用该值。 4. 参数为具有 then 属性的对象: 1. 如果该 then 属性不是方法,则使用该对象。 2. 如果该 then 属性是方法,则该 then 方法接收两个参数,用于将 promise 的状态修改为 fulfilled 或 rejected。
构造函数
Promise
在创建时立即执行
Promise
的构造函数接收一个函数作为参数,该函数有两个参数,这两个参数被通常被命名为 resolve
和 reject
,用于修改 Promise
的状态;这两个函数都可以接收一个参数传递给 Promise
的 then
或 catch
函数
resolve
将 Promise
的状态从 padding
转变为 fulfilled
reject
将 Promise
的状态从 padding
转变为 rejected
then() 方法
then() 方法接收两个参数,分别为 promise 执行 resolve() 的处理程序和 执行 reject() 的处理程序,该函数被调用以响应 promise 内部状态的变化。这两个参数都可以设置为 undefined 表示不需要某个执行程序。
then() 始终返回另一个 promise
then 方法调用时采取的步骤取决于承诺的状态:
如果 promise 的状态为 pending(promise 未解决),then() 只需存储稍后要调用的处理程序。
case "pending": // 多个 then 链式调用时如果 promise 状态为 pending 会将处理程序全部推入队列中 pledge[PledgeSymbol.fulfillReactions].push(fulfillReaction); // fulfillReaction 和 rejectReaction 始终有值,即使没有对应的处理程序 pledge[PledgeSymbol.rejectReactions].push(rejectReaction);
在 promise 状态改变后将队列中的处理程序遍历添加到微任务队列中,注意是将本 promise 的所有 then 方法添加的处理程序全部添加到微任务队列
for (const reaction of reactions) { const job = new PledgeReactionJob(reaction, argument); // PledgeReactionJob 构造函数用来包装处理程序 hostEnqueuePledgeJob(job); }
如果承诺的状态已完成,则 then()立即将作业排队以执行完成处理程序。
case "fulfilled": const value = pledge[PledgeSymbol.result]; const fulfillJob = new PledgeReactionJob(fulfillReaction, value); queueMicrotask(fulfillJob); // 创建微任务
如果承诺的状态被拒绝,则 then()立即将作业排队以执行拒绝处理程序。
case "rejected": const reason = pledge[PledgeSymbol.result]; const rejectJob = new PledgeReactionJob(rejectReaction, reason); // TODO: if [[isHandled]] if false queueMicrotask(rejectJob); break;
catch() 方法
catch() 所做的就是以一参为 undefined,二参为 reject 的处理程序来调用 then()
catch(onRejected) {
return this.then(undefined, onRejected);
}
示例
多个 then 时添加微任务队列的顺序
第一个 Promise 的第二个 then 中返回了一个 Promise.resolve(),打印顺序为 1 5 2 6 7 8 3 4
去掉这个 Promise.resolve() 的话打印顺序为 1 5 2 6 3 7 4 8
Promise.resolve()
.then((res) => {
console.log(1);
})
.then((res) => {
console.log(2);
return Promise.resolve();
})
.then((res) => {
console.log(3);
})
.then(() => {
console.log(4);
});
Promise.resolve()
.then((res) => {
console.log(5);
})
.then((res) => {
console.log(6);
})
.then((res) => {
console.log(7);
})
.then((res) => {
console.log(8);
});
// 1 5 2 6 7 8 3 4
finally() 方法可以有多个
Promise.resolve()
.then(() => {
console.log(1);
})
.finally(() => {
console.log("finally");
return Promise.reject();
})
.catch(() => {
console.log(3);
})
.finally(() => {
console.log("finally");
});