定义MyPromise
首先promise是一个构造函数,它能够接受一个executor参数,该参数是一个函数,该函数又接受两个函数(resolve和reject)作为参数。
class MyPromise {
// 接受一个函数作为参数
constructor(executor){
const resolve = () =>{}
const reject = () =>{}
// 该函数接受两个函数
executor(resolve,reject)
}
}
promise有三种状态(进行中pending、解决resolve、拒绝reject),所以我们定义三个常量
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
promise初始状态是pending,除非外界调用了resolve或者reject函数才会改变状态
constructor(executor){
// 初始状态是pending
this.status = PENDING
}
我们还需要两个变量来分别让reject()和reject()回调使用,代表终值和拒因
constructor(executor){
this.value = undefined
this.reason = undefined
}
promise的状态只能由pending变为fulfilled或者rejected
resolve/reject函数只能使用箭头函数来表示,为了保证this一定指向MyPromise(箭头函数中的this会使用函数定义时父函数中的this,在这里也就是constructor中的this)
constructor(executor) {
// 解决的回调
const resolve = (value) => {
// 状态只能由pending 变为 fulfilled 或者 rejected
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
}
}
// 拒绝的回调
const reject = (reason) => {
// 状态只能由pending 变为 fulfilled 或者 rejected
if (this.status = PENDING) {
this.status = REJECTED
this.reason = reason
}
}
executor(resolve, reject)
}
实现then
接下来实现then方法,根据Promise/A+规范一个promise必须提供一个then方法用来访问当前值,终值和拒因,then方法接受两个参数,then(onFulfilled, onRejected),这个两个参数都是可选的,而且都是函数,不是函数会被忽略
onFulfilled在promise执行结束后会被调用,其第一个参数是promise的终值。
onRejected在promise被拒绝执行后调用,其第一个参数是promise的拒因。
// 接受两个参数onFulfilled和onRejected
then(onFulfilled, onRejected) {
// onFulfilled 和 onRejected 都是可选参数,如果没传的时候就设置默认值
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
onRejected =
typeof onRejected === "function"
? onRejected
: reason => {
throw reason;
};
// onFulfilled在执行结束后会被调用,第一个参数是promise的终值
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
// onRejected在被拒绝后会被调用,第一个参数是promise的拒因
if (this.status === REJECTED) {
onRejected(this.reason);
}
}
异常处理,如果执行过程中发生异常,我们希望promise状态变为拒绝,并抛出异常
// 如果报错将调用reject改变状态,并抛出错误
try {
// 该函数接受两个函数作为参数
executor(resolve, reject);
} catch (e) {
reject(e);
}
处理异步
现在在我们的MyPromise中遇到了异步调用会怎么样?比如在executor中延迟两秒再去调用resolve
// 测试MyPromise
function foo() {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("resolve value");
}, 1000);
});
}
foo().then(
value => {
console.log("resolve", value);
},
reason => {
console.log("reject", reason);
}
);
执行一下可以看到,什么都没有打印出来。根本原因就是在遇到setTimeout的时候,会把回调放到事件循环的宏任务队列中,然后就去执行then()方法了,但是此刻还没调用resolve(),状态还是pending,也就无法处理了。
之所以没有触发onFulfilled()方法,就是在同步代码中执行then()时,我们的异步代码还没调用resolve(),因此MyPromise的状态还是pending
那我们可以then()中处理pending状态,维护一个容器,专门存放onFulfilled(),然后在异步代码调用resolve的时候,从这个容器中依次取出onFulfilled()并执行
// 接受两个参数onFulfilled和onRejected
then(onFulfilled, onRejected) {
// ...
// 状态是pending时,存放onFulfilled和onRejected
if (this.status === PENDING) {
this.onFulfilledCallbackList.push(() => onFulfilled(this.value));
this.onRejectedCallbackList.push(() => onRejected(this.reason));
}
}
// 解决的回调
const resolve = value => {
// 状态只能由pending 变为 fulfilled 或者 rejected
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 从容器中取出onFulfilled并执行
this.onFulfilledCallbackList.map(fn => fn());
}
};
// 拒绝的回调
const reject = reason => {
// 状态只能由pending 变为 fulfilled 或者 rejected
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 从容器中取出onRejected并执行
this.onRejectedCallbackList.map(fn => fn());
}
};
链式调用
下面我们解决链式调用,根据promise/A+ 规范,then()返回的是一个promise对象
那么我们改造一下then(),用MyPromise包裹一下,再返回这个新的实例
// 接受两个参数onFulfilled和onRejected
then(onFulfilled, onRejected) {
...
let promise2 = new MyPromise((resolve, reject) => {
// onFulfilled在执行结束后会被调用,第一个参数是promise的终值
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
// onRejected在被拒绝后会被调用,第一个参数是promise的拒因
if (this.status === REJECTED) {
onRejected(this.reason);
}
// 状态是pending时,存放onFulfilled和onRejected
if (this.status === PENDING) {
this.onFulfilledCallbackList.push(() => onFulfilled(this.value));
this.onRejectedCallbackList.push(() => onRejected(this.value));
}
});
return promise2;
}
如果onFulfilled和onRejected抛出了异常,则promise2必须拒绝执行,并返回拒因 ,那我们就用try/catch捕获一下,遇到异常就调用promise2的reject()
then(onFulfilled, onRejected) {
...
let promise2 = new MyPromise((resolve, reject) => {
// onFulfilled在执行结束后会被调用,第一个参数是promise的终值
if (this.status === FULFILLED) {
try {
let x = onFulfilled(this.value);
} catch (e) {
reject(e);
}
}
// onRejected在被拒绝后会被调用,第一个参数是promise的拒因
if (this.status === REJECTED) {
try {
let x = onRejected(this.reason);
} catch (e) {
reject(e);
}
}
// 状态是pending时,存放onFulfilled和onRejected
if (this.status === PENDING) {
this.onFulfilledCallbackList.push(() => {
try {
let x = onFulfilled(this.value);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbackList.push(() => {
try {
let x = onRejected(this.value);
} catch (e) {
reject(e);
}
});
}
});
return promise2;
}
[[Resolve]](promise2, x)处理x
规范里还提到,如果onFulfilled或者onRejected返回了一个x,则运行Prmoise的解决过程(即:
[[Resolve]](promise2,x)),那我们加上x
then(onFulfilled, onRejected) {
let promise2 = new MyPromise((resolve, reject) => {
// onFulfilled在执行结束后会被调用,第一个参数是promise的终值
if (this.status === FULFILLED) {
try {
let x = onFulfilled(this.value);
} catch (e) {
reject(e);
}
}
// onRejected在被拒绝后会被调用,第一个参数是promise的拒因
if (this.status === REJECTED) {
try {
let x = onRejected(this.reason);
} catch (e) {
reject(e);
}
}
// 状态是pending时,存放onFulfilled和onRejected
if (this.status === PENDING) {
this.onFulfilledCallbackList.push(() => {
try {
let x = onFulfilled(this.value);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbackList.push(() => {
try {
let x = onRejected(this.value);
} catch (e) {
reject(e);
}
});
}
});
return promise2;
}
规范中提到的运行 Promise 解决过程:[[Resolve]](promise2, x),其实就是调用[[Resolve]]函数去处理promise2和x
我们把[[Resolve]]命名为resolvePromiseX,那么我们就要应该在拿到x后去调用一个这样的函数
let promise2 = new MyPromise((resolve, reject) => {
// onFulfilled在执行结束后会被调用,第一个参数是promise的终值
if (this.status === FULFILLED) {
try {
let x = onFulfilled(this.value);
resolvePromiseX(promise2, x);
} catch (e) {
reject(e);
}
}
...
})
异步执行resolvePromise保证参数都能获取到
let promise2 = new MyPromise((resolve, reject) => {
// onFulfilled在执行结束后会被调用,第一个参数是promise的终值
if (this.status === FULFILLED) {
// 以宏任务的方式执行resolvePromise,保证能拿到promise2的实例
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromiseX(promise2, x);
} catch (e) {
reject(e);
}
}, 0);
}
...
})
实现resolvePromiseX
根据规范,如果promise和x 指向同一对象,那么以TypeError为拒因拒绝执行promise
/**
* @description:
* @param {*} promise MyPromise 实例
* @param {*} x 前一个 MyPromise resolve 出来的值
* @param {*} resolve promise 对象构造函数中的 resolve
* @param {*} reject promise 对象构造函数中的 reject
*/
function resolvePromiseX(promise, x, resolve, reject) {
// 如果promise和x指向同一对象,以TypeError为拒因,拒绝执行promise
if (promise === x) {
return reject(new TypeError("Chaining cycle detected for promise #<MyPromise>"));
}
// 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
let isCalled = false;
// 如果x是对象或者函数
if ((typeof x === "object" && x !== null) || typeof x === "function") {
// 如果x.then 的取值时抛出错误e,则以e为拒因拒绝promise
try {
// 把x.then赋值给then
let then = x.then;
// 如果then是函数
if (typeof then === "function") {
// 则将x作为函数的作用域this调用之, 并传递两个回调函数作为参数,第一个参数叫resolvePromise,第二个叫rejectPromise
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
then.call(
x,
y => {
if (isCalled) return;
isCalled = true;
resolvePromiseX(promise, y, resolve, reject);
},
r => {
if (isCalled) return;
isCalled = true;
reject(r);
}
);
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} catch (e) {
if (isCalled) return;
isCalled = true;
reject(e);
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}
实现catch
我们直接调用then,只传入onRejected
catch(onRejected){
this.then(null, onRejected)
}
完整代码
完整代码在我的github 完整代码