手写实现Promise源码
/**
*手写实现MyPromise源码
*/
// promise的三种状态,这里为了方便使用定义为常量,这三个状态一旦确定就无法更改
const PENDING = 'pending'; // 请求状态为等待
const FULFILLED = 'fulfilled'; // 请求状态为成功
const REJECTED = 'rejected'; // 请求状态为失败
/**
* 定义自定义promise类,promise是一个类
* 1、因为是类所以定义这个promise类;
* 2、promise对象中传入的是一个执行器(回调函数,需要立即执行,里面需要将类中的resolve和reject方法传递出去)
* 3、需要根据promise的状态(这个状态是每个promise独有的,需要定义为实例属性)来判断调用哪个resolve还是reject
* 4、then方法(原型对象上的方法)内部做的事情就是判断状态,如果成功则调用执行成功的回调函数,如果失败则调用执行失败的回调函数
* 5、在then方法的成功失败回调函数中,需要把对应的返回值(在resolve和reject中处理)传给这两个函数中;
* 6、为了解决promise执行器中的异步逻辑,即当状态为pending的时候,将成功回调和失败回调存储起来(定义为实例属性),然后在resolve和reject中判断这两个是否存在存在则执行
* 7、因为同一个promise对象的then方法可以多次调用,分为两种情况(同步和异步),同步则立即执行回调函数即可,异步则需要存储起来,因此第六点中的成功回调和失败回调是个数组,需要逐一取出执行
* 8、promise的then方法可以被链式调用,实现链式调用时因为其每个then方法都返回promise对象,其对应回调函数中返回值取决于上个then方法对应回调函数里的返回值,则需要判断这个返回值是否是普通值还是promise对象,是promise对象(不能是当前promise对象)则查看其返回结果,根据返回结果调用resolve还是reject
* 9、 捕获promise中的错误,一个是在执行器中进行捕获,还有一个就是在then方法里的成功回调函数如果执行报错,需要在下个then方法中失败回调函数中捕获这个错误
* 10、then方法中的参数是可选的,因此需要将对于返回值一层一层往下传递
* 11、promise.all静态方法,传入数组返回的也是数组,
* 12、promise.resolve静态方法会创建一个promise对象,将传入的参数包裹在这个promise对象里
* 13、finally方法无论状态成功还是失败都会被执行一次 ,这个方法后面能链式调用then方法
* 14、catch方法执行失败回调,也就只要在这个方法里调用then方法,只注册失败回调,没有注册成功回调,这个方法后面也能调用promise其他的方法,因此需要返回这个promise对象
*/
class MyPromise {
// 传递一个执行器,这个方法会立即执行
constructor(executor) {
try {
// 将resolve和reject方法传递出去,因此需要在类中定义该方法
executor(this.resolve, this.reject)
} catch (e) {
// 捕获异常错误
this.reject(e);
}
}
// promise 状态 这个状态是每个promise独有的,需要定义为实例属性
status = PENDING;
// 成功之后的值(每个promise对象都有成功之后的值,将其保存下来,定义为实例属性,默认值为undefined)
value = undefined;
// 失败后的原因
reason = undefined;
// 成功回调,因为promise可以多次调用then方法,所以用的是数组存储起来
successCallback = [];
// 失败回调,因为promise可以多次调用then方法,所以用的是数组存储起来
failCallback = [];
// 定义resolve方法,参数value是返回值,使用箭头函数的原因是为了处理方法中this的指向问题,这里指向promise对象
resolve = value => {
// 判断如果状态不是等待,则阻止程序向下执行,防止更改promise状态
if (this.status !== PENDING) return;
// 将状态更改为成功
this.status = FULFILLED;
// 保存成功之后的值
this.value = value;
// 判断成功回调是否存在 如果存在 调用(因为then方法可以多次调用,需要存储对应回调,取出执行)
// this.successCallback && this.successCallback(this.value);
// 当成功回调数组是否大于0,大于0执行成功回调
while (this.successCallback.length) this.successCallback.shift()(this.value)
}
// 定义reject方法,将失败原因传入
reject = reason => {
// 如果状态不是等待,则阻止程序向下执行,防止更改promise状态
if (this.status !== PENDING) return;
// 将状态更改为失败
this.status = REJECTED;
// 保存失败后的原因
this.reason = reason;
// 判断失败回调是否存在 如果存在 调用
// this.failCallback && this.failCallback(this.reason);
while (this.failCallback.length) this.failCallback.shift()(this.reason)
}
/**
* 成功的回调函数,失败的回调函数
* then方法返回的是一个promise对象,所以在里面在命名一个promise对象
*/
then(successCallback, failCallback) {
// 参数可选,因为successCallback, failCallback不是必传参数
successCallback = successCallback ? successCallback : value => value;
// 参数可选
failCallback = failCallback ? failCallback : reason => { throw reason };
// 因为在then方法中需要返回一个promise对象(为了实现then的链式调用),所以在里面在命名一个promise对象 ,其传入的执行器中代码是立即执行的
let promsie2 = new MyPromise((resolve, reject) => {
// 判断状态,如果成功者调用成功的回调函数
if (this.status === FULFILLED) {
setTimeout(() => {
try {
// 为了在链式调用中将上一个then中成功回调返回值,传给下一个then中,用x接收返回值
let x = successCallback(this.value);
/**
* 在这里需要判断 x 的值是普通值还是promise对象
* 如果是普通值 直接调用下一个promise对象的resolve
* 如果是promise对象 查看promise对象返回的结果
* 再根据promise对象返回的结果 决定调用resolve 还是调用reject
* 因此这需要写在公共方法中
* 这里为了防止promise对象返回自身报错,需要传入当前promise2,但是这里处在新建promise过程中是获取不到对象的,因此用计时器来解决这个问题
*/
resolvePromise(promsie2, x, resolve, reject)
} catch (e) {
// 捕获上一个then方法promise中成功回调函数中的执行错误
reject(e);
}
}, 0)
} else if (this.status === REJECTED) {
// 失败回调
setTimeout(() => {
try {
let x = failCallback(this.reason);
/**
* 判断 x 的值是普通值还是promise对象
* 如果是普通值 直接调用下一个promise对象里的resolve
* 如果是promise对象 查看promise对象返回的结果
* 再根据promise对象返回的结果 决定调用resolve 还是调用reject
* 这里为了防止promise对象返回自身报错,需要传入当前promise2,但是这里处在新建promise过程中是获取不到对象的,因此用计时器来解决这个问题
*/
resolvePromise(promsie2, x, resolve, reject)
} catch (e) {
// 捕获上一个then方法promise中失败回调函数中的执行错误,传递给下个then方法中的失败回调函数
reject(e);
}
}, 0)
} else {
//等待,处理异步请求 将成功回调和失败回调存储起来
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value);
/**
* 判断 x 的值是普通值还是promise对象
* 如果是普通值 直接调用下一个promise对象里的resolve
* 如果是promise对象 查看promise对象返回的结果
* 再根据promise对象返回的结果 决定调用resolve 还是调用reject
* 这里为了防止promise对象返回自身报错,需要传入当前promise2,但是这里处在新建promise过程中是获取不到对象的,因此用计时器来解决这个问题
*/
resolvePromise(promsie2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
});
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
resolvePromise(promsie2, x, resolve, reject)
} catch (e) {
reject(e);
}
}, 0)
});
}
});
return promsie2;
}
// 无论成功还是失败都要调用finally中的callback,如果callback有返回值(普通值还是promise),则需要调用promise.resolve将返回值转换为promise
finally(callback) {
return this.then(value => {
// 为了能在then方法后面继续调用then方法需要在callback(callback可能再return一个promise,需要在这个promise执行之后return value);
return MyPromise.resolve(callback()).then(() => value);
}, reason => {
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}
// 处理当前promise失败回调的方法
catch(failCallback) {
return this.then(undefined, failCallback)
}
// promise中all静态方法,接收一个数组作为参数,返回的数据(也是数组)
static all(array) {
let result = [];
let index = 0;
return new MyPromise((resolve, reject) => {
// 将返回结果组合为一个数组处理
function addData(key, value) {
result[key] = value;
index++;
// 判断数组是否执行完成
if (index === array.length) {
resolve(result);
}
}
// 循环判断all方法传入的值是promise对象还是普通值
for (let i = 0; i < array.length; i++) {
let current = array[i];
if (current instanceof MyPromise) {
// promise 对象,因为有异步操作,需要根据index来判断是否执行完成
current.then(value => addData(i, value), reason => reject(reason))
} else {
// 普通值
addData(i, array[i]);
}
}
})
}
// 只要有一个成功或者失败就返回
static race(array) {
let promise = new MyPromise((resolve, reject) => {
for (let i = 0; i < array.length; i++) {
let curr = array[i];
// MyPromise实例 结果处理
if (curr instanceof MyPromise) {
curr.then(resolve, reject);
} else {
// 非MyPromise实例处理
resolve(curr);
}
}
});
return promise;
}
static resolve(value) {
// 判断传入的参数是否是promise对象,是的话直接返回,不是的话创建并返回一个promise对象并将value通过resolve传递出去
if (value instanceof MyPromise) return value;
return new MyPromise(resolve => resolve(value));
}
}
/**
*
* 处理then回调函数中返回值
* @param {*} promsie2
* @param {*} x 返回值
* @param {*} resolve 当前promise2对象的resolve
* @param {*} reject 当前promise2对象的reject
* @returns
*/
function resolvePromise(promsie2, x, resolve, reject) {
// 判断处理返回值是否是是当前promise对象
if (promsie2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
// 判断返回值是否是promise对象
if (x instanceof MyPromise) {
// promise 对象
x.then(value => resolve(value), reason => reject(reason));
} else {
// 普通值
resolve(x);
}
}
module.exports = MyPromise;