写在开头:如果被大佬们看到了有错误还请斧正,如果把萌新带到了沟里,你在爬出来就好了(手动滑稽)
在 Promise 异步逻辑的实现中实现了对于异步代码逻辑的优化,接下来要做的就是关于 then 方法的优化了
在 Promise 中 then 方法是可以多次调用的。
在全是同步执行的情况下,会立即知道每一个 Promise 目前的状态是成功或失败,所以这里的代码没有问题,但是如果在执行 Promise 的时候,有异步的情况就会出现问题了。(见下图)
原因是在定义成功和失败的回调的时候,只定义了一个值,没有考虑到 then 方法被多次在异步中调用的情况,所以在这里应该将成功和失败的回调定义成为一个可收纳多个值的东西--数组。
successCallback = [];
failCallback = [];
所以 then 方法中的回调存储也随之改变:
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
到目前为止多个回调函数都已经被存在了成功/失败的回调函数数组中了,当状态变为成功/失败的时候就应该依次调用回调函数数组中的每一项,所以 resolve/reject 中应该改为
resolve = value => {
if (this.status !== PENDING) return;
this.status = FULFILLED;
this.value = value;
while (this.successCallback.length) {
// 数组的第一项应该先被执行,且数组第一项就是需要的回调函数所以可以直接调用
this.successCallBack.shift()(this.value);
}
}
reject = value => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason;
while (this.failCallback.length) {
// 同理 resolve
this.failCallback.shift()(this.reason);
}
}
Promise 中 then 方法的可以被多次调用就完成了,then 方法除了能被多次调用还能够被链式调用:
promise.then().then().then()
后一个 then 方法拿到的调用值,就是前一个 then 方法返回的返回值。
所以接下来要实现的是 then 方法的链式调用。
先分析一下 then 方法:
- then 方法是 Promise 中的
- then 方法既然能够链式调用,那么 then 方法的返回值就是一个 Promise 对象
// 既然 then 方法返回的是一个 Promise 对象,那就创建一个 Promise 对象让它返回
then(successCallback, failCallback) {
let thenPromise = new myPromise(() => {
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.reason);
}
});
return thenPromise;
};
这样 then 方法就返回了一个 Promise 对象可以给到下一个调用的 then 了。现在就需要考虑下一个 then 方法该如何拿到这个被前一个 then 返回了的 Promise 对象了。
then(successCallback, failCallback) {
let thenPromise = new myPromise((resolve, reject) => {
// 当成功/失败的时候就能得到这个成功/失败的回调,所以在这里也能够拿到这个的返回值
if (this.status === FULFILLED) {
// 这个 x 就是这个成功回调的返回值
let x = successCallback(this.value);
resolve(x);
} else if (this.status === REJECTED) {
let x = failCallback(this.reason);
reject(x);
}
});
return thenPromise;
};
这样 then 方法就可以返回一个 Promise 对象了。可如果这个返回值就是一个 Promise 对象咋整?(Promise 里套一个 Promise 返回?报不报错另说,这么写了估计离被打死不远了…)
所以接下来需要做的是:判断 x 的值是普通值还是 Promise 对象;如果是普通值直接 resolve;如果是 Promise 对象,在根据 Promise对象的返回结果来判断使用 resolve 还是 reject ;
// 这里写成一个函数方便复用
function argParsing(x, resolve, reject) {
// Q: 该怎么判断这个 x 是不是一个 Promise对象
// A: 判断 x 是不是 myPromise 的一个实例对象
if (x instanceof myPromise) {
x.then(value => {
resolve(value);
}, reason => {
reject(reason);
});
// 上面这一堆也可以简写成 x.then(resolve, reject);
} else {
resolve(x);
}
}
检查 x 是不是一个 Promise 对象就写好了,现在调用它试试
then(successCallback, failCallback) {
let thenPromise = new myPromise((resolve, reject) => {
if (this.status === FULFILLED) {
let x = successCallback(this.value);
argParsing(x, resolve, reject);
} else if (this.status === REJECTED) {
let x = failCallback(this.reason);
argParsing(x, resolve, reject);
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
})
return thenPromise;
}
then 方法的链式调用就这样完成了。
所以到目前为止实现的 Promise 代码应该长成这样:
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected'
class myPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING;
value = undefined;
reason = undefined;
successCallback = [];
failCallback = [];
resolve = (value) => {
if (this.status !== PENDING) return;
this.status = FULFILLED;
this.value = value;
while (this.successCallback.length) {
this.successCallback.shift()(this.value);
}
}
reject = (reason) => {
if (this.status !== PENDING) return;
this.status = REJECTED;
this.reason = reason;
while (this.failCallback.length) {
this.failCallback.shift()(this.reason);
}
}
then(successCallback, failCallback) {
let thenPromise = new myPromise((resolve, reject) => {
if (this.status === FULFILLED) {
let x = successCallback(this.value);
argParsing(x, resolve, reject);
} else if (this.status === REJECTED) {
let x = failCallback(this.reason);
argParsing(x, resolve, reject);
} else {
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
})
return thenPromise;
}
}
function argParsing(x, resolve, reject) {
if (x instanceof myPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}