手写Promise,Promise.all,Promise.race,Promise. allSettled

464 阅读4分钟

前言

我们引入了Promise,它允许我们简化异步流程,避免回调的麻烦。手写Promise,那我是不是也要写一个瞅瞅呢?

实现思路

  1. 构造函数,该构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数——resolve 和 reject ——作为其参数。
  2. 当异步任务顺利完成且返回结果值时,会调用 resolve 函数;而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用reject 函数。
  3. 构造函数结构,状态管理,state,暂存异步后的返回值,暂存异步成功后后需要处理的函数,暂存异步处理后失败需要处理的函数

代码实现(Promise)

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function MyPromise(executor) {
    const _this = this;
    this.state = PENDING;
    this.value = undefined;
    this.onFulfilled = [];
    this.onRejected = [];
    function resolve(value) {
        if(_this.state === PENDING){
            _this.state = FULFILLED;
            _this.value = value;
            // for(let i in _this.onFulfilled){
            //     _this.onFulfilled[i](value)
            // }
        }
    }
    function reject(value) {
        if(_this.state === PENDING){
            console.log('reject')
            _this.state = REJECTED;
            _this.value = value;
            // for(let i in _this.onRejected){
            //     _this.onRejected[i](value)
            // }
        }
    }
    try {
        console.log(111)
        executor(resolve,reject);
    } catch (error) {
        console.log(22)
        reject(error);
    }
}

MyPromise.prototype.then =  function(onFulfilled,onRejected) {
    console.log('执行then方法')
    console.log(this.state,'this.state')
    if(this.state === FULFILLED){
        typeof onFulfilled === 'function' && onFulfilled(this.value)
    }
    if(this.state === REJECTED){
        typeof onRejected === 'function' && onRejected(this.reason)
    }
    // if(this.state === PENDING){
    //     typeof onFulfilled === 'function' && this.onFulfilled.push(onFulfilled)
    //     typeof onRejected === 'function' && this.onRejected.push(onRejected)
    // }
}
new MyPromise((a,b) => {
    console.log('异步请求开始')
    setTimeout(() => {
        a('异步请求结束')
    },2000)
    console.log('执行异步请求')
}).then((res) => {
    console.log(res)
});
console.log(888)

此处输出的结果是:

image.png

会发现没有打印出异步请求结束的语句,经过分析发现:当then里面函数运行时,resolve由于是异步执行的,还没有来得及修改state,所以执行then时state的状态会是PENDING状态,不会执行onFulfilled和onRejected函数;所以需要处理将成功或失败的函数放到数组中暂存起来,当异步响应成功过或失败时去执行对应的函数,即打开上述代码中注释掉的部分即可。

Promise链式调用

上述的 MyPromise 构造函数是.then方法是没有返回值的,如果想支持链式调用必须有返回值,且返回值包含then方法,那么我们只需要在.then方法中返回一个新的MyPromise实例即可:

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function MyPromise(executor) {
    const _this = this;
    this.state = PENDING;
    this.value = undefined;
    this.onFulfilled = [];
    this.onRejected = [];
    function resolve(value) {
        if(_this.state === PENDING){
            _this.state = FULFILLED;
            _this.value = value;
            for(let i in _this.onFulfilled){
                _this.onFulfilled[i](_this.value)
            }
        }
    }
    function reject(value) {
        if(_this.state === PENDING){
            console.log('reject')
            _this.state = REJECTED;
            _this.value = value;
            for(let i in _this.onRejected){
                _this.onRejected[i](_this.value)
            }
        }
    }
    try {
        executor(resolve,reject);
    } catch (error) {
        reject(error);
    }
}

MyPromise.prototype.then =  function(onFulfilled,onRejected) {
    let that = this;
   return new MyPromise((resolve,reject) => {
        if(that.state === FULFILLED && typeof onFulfilled === 'function'){
            let res = onFulfilled(that.value)
            resolve(res);
        }
        if(that.state === REJECTED && typeof onRejected === 'function'){
             let error = onRejected(that.reason)
             reject(error);
        }
        if(that.state === PENDING){
             that.onFulfilled.push(() => {
                const res = onFulfilled(that.value)
                resolve(res)
             })
            that.onRejected.push(() => {
                const reason = onRejected(that.reason)
                reject(reason)
            })
        }
    })
}
new MyPromise((a,b) => {
    setTimeout(() => {
        a('66666')
    },2000)
}).then((res) => {
    console.log(res,'mmmm')
    return '异步请求结果2';
}).then(res => {
    console.log(res,'ssss')
});

代码实现(Promise.all)

  1. 分析:
    • 该方法挂在原型上
    • 返回结果也是promise
    • 传入输入数组,需要对传入参数做类型判断
    • 遍历数组,数组元素,可能不是promise,需要做类型判断,如果判断可以手动转成promise
    • 所有传入promsie全部处理完成返回结果,且传入需要和输入出结果一一对应,如何一一对应呢,可以用计数器
  2. 特性:传入的promise全部成功则成功,一个失败则整体失败
  3. 代码:
    Promise.all = function(promiseArray){
        return new Promise((resolve,reject) => {
            if(!Array.isArray(promiseArray)) return new typeError('参数错误');
            let len = promiseArray.length;
            let resultArr = [];
            let counter = 0;
            for(let i = 0; i < len; i++ ){
                Promise.resolve(promiseArray[i]).then(res => {
                    counter++;
                    resultArr[i] = res;
                    if(counter === len) resolve(resultArr);
                }).catch(error => {
                    reject(error)
                })
            }
        })
    }
  1. 测试用例
let p1 = new Promise((resolve, reject) => {
	setTimeout(() => {
		console.log("1", 1);
		resolve(1);
	}, 1000);
});
let p2 = new Promise((resolve, reject) => {
	setTimeout(() => {
		console.log("2", 2);
		resolve(2);
	}, 2000);
});
let p3 = new Promise((resolve, reject) => {
	setTimeout(() => {
		console.log("3", 3);
		resolve(3);
	}, 3000);
});

输出结果: // 1 1 // 2 2 // 3 3 // res [ 1, 2, 3 ]

代码实现(Promise.race)

  1. 分析:
    • 该方法挂在原型上
    • 返回结果也是promise
    • 传入输入数组,需要对传入参数做类型判断
    • 遍历数组,数组元素,可能不是promise,需要做类型判断,如果判断可以手动转成promise
    • 返回结果只需返回第一个处理完成的promise
  2. 特性:直接返回第一个响应的promise,无论成功失败
  3. 代码:
Promise.race = function (promiseArray) {
	return new Promise((resolve, reject) => {
		if (!Array.isArray(promiseArray)) return new typeError("参数错误");
		let len = promiseArray.length;
		let resultArr = [];
		let counter = 0;
		for (let i = 0; i < len; i++) {
			Promise.resolve(promiseArray[i])
				.then(res => {
					counter++;
					if (counter === 1) {
						resolve(res);
					}
				})
				.catch(error => {
					reject(error);
				});
		}
	});
};

代码实现(Promise.allSettled)

  1. 分析:
    • 该方法挂在原型上
    • 返回结果也是promise
    • 传入输入数组,需要对传入参数做类型判断
    • 遍历数组,数组元素,可能不是promise,需要做类型判断,如果判断可以手动转成promise
    • 返回结果无论成功失败,全部处理完之后统一返回结果
  2. 特性:无论成功失败,全部处理完之后统一返回
  3. 代码:
Promise.allSettled = function (promiseArray) {
	return new Promise((resolve, reject) => {
		if (!Array.isArray(promiseArray)) return new typeError("参数错误");
		let len = promiseArray.length;
		let resultArr = [];
		let counter = 0;
		for (let i = 0; i < len; i++) {
			Promise.resolve(promiseArray[i])
				.then(res => {
					counter++;
					resultArr[i] = { staus: "status", value: res };
					if (counter === len) {
						resolve(resultArr);
					}
				})
				.catch(error => {
					counter++;
					resultArr[i] = { staus: "status", reason: error };
					resolve(resultArr);
				});
		}
	});
};
  1. 测试用例
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, "foo"));
const promises = [promise1, promise2];

Promise.allSettled(promises).then(results => results.forEach(result => console.log(result)));

结果:

image.png