返回
Promise 源码剖析:同步执行 resolve 的缺陷与改进
前端
2023-09-04 20:05:35
Promise 源码:同步执行 resolve
在上一篇《Promise 源码:实现一个简单的 Promise》当中,我们实现了一个可以简单可用的 Promise。但它实际上还是有不少的缺陷的,比如:
- Promise 构造函数里直接同步 resolve,则执行不到 then。
- 只有 resolve,没有 reject。
一些开发者提出了一种解决方案,称作 microtask 队列。
microtask 队列
microtask 队列是 JavaScript 引擎维护的一个队列,里面存储了待执行的微任务(microtask)。当 JavaScript 引擎主线程执行完毕后,会在执行宏任务(macrotask)之前执行该队列中的所有微任务。
一些浏览器(如 Chrome)将微任务和宏任务都存储在同一个队列中,按顺序执行;另一些浏览器(如 Firefox)将它们存储在不同的队列中,优先执行微任务队列。
缺陷的改进
借助 microtask 队列,我们可以改进之前的 Promise 缺陷:
class Promise {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.state !== 'pending') return;
this.state = 'fulfilled';
this.value = value;
// 将 then 回调函数放入 microtask 队列中
queueMicrotask(() => {
this.onFulfilledCallbacks.forEach((callback) => {
callback(value);
});
});
};
const reject = (reason) => {
if (this.state !== 'pending') return;
this.state = 'rejected';
this.reason = reason;
// 将 then 回调函数放入 microtask 队列中
queueMicrotask(() => {
this.onRejectedCallbacks.forEach((callback) => {
callback(reason);
});
});
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
return new Promise((resolve, reject) => {
const fulfilled = (value) => {
try {
const result = typeof onFulfilled === 'function' ? onFulfilled(value) : value;
resolve(result);
} catch (error) {
reject(error);
}
};
const rejected = (reason) => {
try {
const result = typeof onRejected === 'function' ? onRejected(reason) : reason;
resolve(result);
} catch (error) {
reject(error);
}
};
if (this.state === 'pending') {
this.onFulfilledCallbacks.push(fulfilled);
this.onRejectedCallbacks.push(rejected);
} else if (this.state === 'fulfilled') {
queueMicrotask(() => fulfilled(this.value));
} else if (this.state === 'rejected') {
queueMicrotask(() => rejected(this.reason));
}
});
}
}
在改进后的 Promise 中,无论 Promise 构造函数里 resolve 还是 reject,都会将 then 回调函数放入 microtask 队列中,从而保证 then 回调函数在微任务队列中执行,而不会因为同步执行 resolve 而执行不到。