【JavaScript】promise实现原理解析
观察者模式与 Promise A+ 规范
promise 可以看成是观察者模式的实现:
then 收集依赖
->异步触发 resolve
->resolve 执行依赖
。
ES6 的 promise 实现遵循 Promise/A+规范,这里只总结两条规则:
- Promise 本质是一个状态机,且状态只能为以下三种:Pending(等待态)、Fulfilled(执行态)、Rejected(拒绝态),状态的变更是单向的,只能从
Pending -> Fulfilled
或Pending -> Rejected
,状态变更不可逆。 - then 方法接收两个可选参数,分别对应状态改变时触发的回调。then 方法返回一个 promise。then 方法可以被同一个 promise 调用多次。
class MyPromise {
// Promise/A+规范的三种状态
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this._value = undefined; // 记录最后一次then的值
this._status = MyPromise.PENDING;
this._resolveQueue = [];
this._rejectQueue = [];
const _resolve = (val) => {
if (this._status !== MyPromise.PENDING) return;
this._value = val;
this._status = MyPromise.FULFILLED;
while (this._resolveQueue.length) {
const callback = this._resolveQueue.shift();
callback();
}
};
const _reject = (val) => {
if (this._status !== MyPromise.PENDING) return;
this._value = val;
this._status = MyPromise.REJECTED;
while (this._rejectQueue.length) {
const callback = this._rejectQueue.shift();
callback();
}
};
executor(_resolve, _reject);
}
then(resolveFn, rejectFn) {
this._resolveQueue.push(resolveFn);
this._rejectQueue.push(rejectFn);
}
}
这种实现方式可以让我们在 then
方法中获取到异步操作的返回值:
new MyPromise((resolve) => {
setTimeout(() => {
resolve(9);
}, 500);
}).then(console.log);
// 500ms 后输出 9
then 的异步链式调用
promise 的 .then()
方法支持异步链式调用:
new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 500);
})
.then((res) => {
console.log(res);
return new Promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 500);
});
})
.then(console.log);
思考如何实现这种链式调用:
.then()
需要返回一个 promise,这样才能找到 then 方法,所以要把 then 方法的返回值包装成 promise。.then()
需要按顺序执行,如果回调返回一个新的 promise,下一个.then()
需要等到这个 promise 状态变更之后执行。.then()
的回调需要拿到上一个.then()
的返回值。
实现类似 promise 的异步链式调用中对这种封装思路进行了解析。
class MyPromise {
// ...
then(resolveFn, rejectFn) {
return new MyPromise((resolve, reject) => {
// 封装 resolveFn 回调的执行结果
const fulfilledFn = () => {
try {
const res = resolveFn(this._value);
if (res instanceof MyPromise) {
res.then(resolve);
} else {
resolve(res);
}
} catch (error) {
reject(error);
}
};
this._resolveQueue.push(fulfilledFn);
// 封装 rejectFn 回调的执行结果
const rejectedFn = () => {
try {
const res = rejectFn(this._value);
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
} catch (err) {
reject(err);
}
};
this._rejectQueue.push(rejectedFn);
});
}
}
值穿透&状态已变更的情况&兼容同步任务
值穿透
根据规范,如果 then 接收的参数不为 function,我们应该忽略它,并将上一个 then 的返回值传递到下一个 then 的回调中。
状态已变更的情况
上面的实现中只在 resolve 之后开始执行回调,而在实际使用中会出现在 resolve 之后调用 then 的情况:
Promise.resolve().then()
。对于状态已经变更为
fulfilled
或rejected
的情况时,直接执行 then 回调。兼容同步任务
上面的实现中都默认 executor 中是异步方法,如果 executor 是同步方法,会存在一些问题。对于下面这个例子而言,期望的打印顺序是:
1 4 2 3
,实际打印顺序是:1 2 4 3
。为了兼容同步任务,我们给 resolve/reject 执行回调的操作包一个 setTimeout,让它异步执行。
console.log(1); const a = new MyPromise((resolve) => { resolve(2); }) .then(console.log) .then(() => { return new MyPromise((resolve) => { setTimeout(() => { resolve(3); }, 500); }); }) .then(console.log); console.log(4);
完善后的完整代码
class MyPromise {
// Promise/A+规范的三种状态
static PENDING = "pending";
static FULFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this._status = MyPromise.PENDING;
this._resolveQueue = [];
this._rejectQueue = [];
const _resolve = (val) => {
const run = () => {
if (this._status !== MyPromise.PENDING) return;
this._value = val;
this._status = MyPromise.FULFILLED;
while (this._resolveQueue.length) {
const callback = this._resolveQueue.shift();
callback();
}
};
setTimeout(run);
};
const _reject = (val) => {
const run = () => {
if (this._status !== MyPromise.PENDING) return;
this._value = val;
this._status = MyPromise.REJECTED;
while (this._rejectQueue.length) {
const callback = this._rejectQueue.shift();
callback();
}
};
setTimeout(run);
};
executor(_resolve, _reject);
}
then(resolveFn, rejectFn) {
// 根据规范,如果then的参数不是function,则我们需要忽略它, 让链式调用继续往下执行
if (typeof resolveFn !== "function") {
resolveFn = (value) => value;
}
if (typeof rejectFn !== "function") {
rejectFn = (reason) => {
throw new Error(reason instanceof Error ? reason.message : reason);
};
}
return new MyPromise((resolve, reject) => {
// 封装 resolveFn 回调的执行结果
const fulfilledFn = () => {
try {
const res = resolveFn(this._value);
if (res instanceof MyPromise) {
res.then(resolve);
} else {
resolve(res);
}
} catch (error) {
reject(error);
}
};
// 封装 rejectFn 回调的执行结果
const rejectedFn = () => {
try {
const res = rejectFn(this._value);
if (res instanceof MyPromise) {
res.then(resolve, reject);
} else {
resolve(res);
}
} catch (err) {
reject(err);
}
};
switch (this._status) {
// 当状态为pending时,把then回调push进resolve/reject执行队列,等待执行
case MyPromise.PENDING:
this._resolveQueue.push(fulfilledFn);
this._rejectQueue.push(rejectedFn);
break;
// 当状态已经变为resolve/reject时,直接执行then回调
case MyPromise.FULFILLED:
fulfilledFn(this._value); // this._value是上一个then回调return的值(见完整版代码)
break;
case MyPromise.REJECTED:
rejectedFn(this._value);
break;
}
});
}
}
其他方法
Promise.prototype.catch()
// catch方法其实就是执行一下then的第二个回调
catch(rejectFn) {
return this.then(undefined, rejectFn)
}
Promise.prototype.finally()
// finally方法
finally(callback) {
return this.then(
// MyPromise.resolve执行回调,并在then中return结果传递给后面的Promise
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => { throw reason }) // reject同理
)
}
Promise.resolve()
// 静态 resolve 方法
static resolve(value) {
if(value instanceof MyPromise) return value // 根据规范, 如果参数是Promise实例, 直接return这个实例
return new MyPromise(resolve => resolve(value))
}
Promise.reject()
// 静态 reject 方法
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
Promise.all()
Promise.all(iterable)
方法接收一个可迭代对象作为参数,待其中的所有 Promise 都 fulfilled 之后执行其回调。
iterable 中的元素如果不是 Promise 对象则直接返回。
如果有一个 Promise 的状态变为 rejected,则 reject() 这个 Promise 的值,否则 resolve() 所有 Promise 值的数组。
class MyPromise {
// ...
static all(promiseArr) {
const result = [];
return new MyPromise((resolve, reject) => {
promiseArr.forEach((promiseEvent) => {
MyPromise.resolve(promiseEvent)
.then((value) => {
result.push(value);
if (result.length === promiseArr.length) {
resolve(result);
}
})
.catch(reject);
});
});
}
}
Promise.race()
Promise.race(iterable)
接收的参数与 Promise.race()
方法相同,区别在于 race 方法的返回值为 iterable 中第一个状态变化的 Promise 的返回值(状态也与该 Promise 一致)。
class MyPromise {
static race(promiseArr) {
return new MyPromise((resolve, reject) => {
promiseArr.forEach((promiseEvent) => {
MyPromise.resolve(promiseEvent).then(resolve).catch(reject);
});
});
}
}