then方法的链式调用
要实现then方法的链式调用,首先要明确的一点是,只有then方法返回Promise对象才可以,另外每一个then方法需要将返回值传递给下一个then方法,而返回值分为普通值和promise对象两种情况
上图红框内的then方法重构如下:
then = (successCallback, failCallback) => {
// then方法在一定情况下,可以不传递参数,但是可以将返回值依次传递给后面的then方法,直到传递给有回调的then方法
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => { throw reason };
const promise = new MyPromise((resolve, reject) => {
if(this.status === FULFILLED) {
const x = successCallback(this.value);
// 判断 x 的类型,是普通值,还是promise对象
// 如果 x 时普通值,则直接调用resolve返回
// 如果 x 是promise对象,则调用then方法,获取状态,并根据状态调用相对应的回调函数
if(x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x);
}
} else if(this.status === REJECTED) {
const y = failCallback(this.reason);
resolve(y);
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promise;
}
然而,有一种特殊情况,即then方法回调为promise时,这个promise不能为当前这个promise,如果是当前的promise就会出现死循环式的调用,为避免此类情况,代码修改如下
then = (successCallback, failCallback) => {
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => { throw reason };
const promise = new MyPromise((resolve, reject) => {
if(this.status === FULFILLED) {
const x = successCallback(this.value);
// 判断 x 的类型,是普通值,还是promise对象
// 如果 x 时普通值,则直接调用resolve返回
// 如果 x 是promise对象,则调用then方法,获取状态,并根据状态调用相对应的回调函数
if (promise === x) { // 判断当前的x是否为本身的promise
return reject(new TypeError('Chaining circle detected for promise #<Promise>'));
}
if(x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x);
}
} else if(this.status === REJECTED) {
const y = failCallback(this.reason);
resolve(y);
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promise;
}
此处代码需要注意的是在promise里面,同步代码是不能获取到还没有声明完成的promise的,
所以需要将代码改为异步(使用setTimeout包裹代码),代码才能够获取到:
then = (successCallback, failCallback) => {
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => { throw reason };
const promise = new MyPromise((resolve, reject) => {
if(this.status === FULFILLED) {
setTimeout(() => {
const x = successCallback(this.value);
// 判断 x 的类型,是普通值,还是promise对象
// 如果 x 时普通值,则直接调用resolve返回
// 如果 x 是promise对象,则调用then方法,获取状态,并根据状态调用相对应的回调函数
if (promise === x) { // 判断当前的x是否为本身的promise
return reject(new TypeError('Chaining circle detected for promise #<Promise>'));
}
if(x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x);
}
}, 0);
} else if(this.status === REJECTED) {
const y = failCallback(this.reason);
resolve(y);
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
return promise;
}
当前代码,只处理了then方法中成功回调的情况,在失败回调和异步模块也需要同样的处理方法,简化代码,需要将公共部分提取成一个公共方法,如下
function resolvePromise(promise, x, resolve, reject) {
if (promise === x) return reject(new TypeError('Chaining circle detected for promise #<Promise>'));
if(x instanceof MyPromise) {
x.then(resolve, reject)
} else {
resolve(x);
}
}
进一步改造then方法如下
then = (successCallback, failCallback) => {
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => { throw reason };
const promise = new MyPromise((resolve, reject) => {
if(this.status === FULFILLED) {
setTimeout(() => {
const x = successCallback(this.value);
resolvePromise(promise, x, resolve, reject);
}, 0);
} else if (this.status === REJECTED) {
setTimeout(() => {
const x = failCallback(this.reason);
resolvePromise(promise, x, resolve, reject);
}, 0);
} else {
this.successCallback.push(() => {
setTimeout(() => {
const x = successCallback(this.value);
resolvePromise(promise, x, resolve, reject);
}, 0);
});
this.failCallback.push(() => {
setTimeout(() => {
const x = failCallback(this.reason);
resolvePromise(promise, x, resolve, reject);
}, 0);
});
}
});
}
又因为then方法中处理异步的部分,返回值处理好了,所以在resolve和reject方法中,调用的时候,就不需要进行传值操作,改动如下
resolve = value => {
if (this.status !== PENDING) return;
this.status = FULFILLED;
this.value = value;
// while(this.successCallback.length) this.successCallback.shift()(this.value);
while(this.successCallback.length) this.successCallback.shift()();
}
reject = reason => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason;
// while (this.failCallback.length) this.failCallback.shift()(this.reason);
while (this.failCallback.length) this.failCallback.shift()(this.reason);
}