手写实现Promise

130 阅读5分钟

引用资料

要实现的API

Promise 解释

  1. Promise 大部分的方法都会返回一个Promise对象,因为我们要做链式调用;
  2. 调用then,catch等方法时,可能会出现三种状态,fulfilled,rejected,pending,前面两种相对容易理解,pending的话则需要使用数组来保存回调方法,并在resolve/reject时清空数组里面的回调列表;
  3. then/catch方法传入的参数都是一个回调方法,回调方法返回的数据会在返回的Promise对象中的result/error属性中;

Promise 构造方法

Promise 构造方法使用例子

let p = new Promise((resolve,reject)=>{
    resolve('result');
    // 或者  reject('error');
})

所以Promise 构造方法肯定是接收一个方法,我们可以根据其特性反推出其构造方法

class Promise{
    // onFulfilledArray 和 onRejectArray 两个数组的意义就是处理异步逻辑
    // 打个比方,假设在reject 之前,通过.then 绑定几个方法,就会写入两个数组中了.
	private onFulfilledArray : Function[] = [];
	private onRejectedArray : Function[] = [];
	status : PROMISE_STATUS = PROMISE_STATUS.PENDING;
	result : any = null;
	error : any = null;
	constructor(apply: Function){
		const onFulfill = (result)=>{
			if(this.status === PROMISE_STATUS.PENDING){
				this.status = PROMISE_STATUS.FULFIL;
				this.result = result;
                                // resolve时把onFulfilledArray中绑定的方法全部执行了
				this.onFulfilledArray.forEach(fun=>{
					fun(result);
				})
			}
		},
			onReject = (error)=>{
				if(this.status === PROMISE_STATUS.PENDING){
					this.status = PROMISE_STATUS.REJECT;
					this.error = error;
                                        // reject 把数组里面的方法全部执行了
					this.onRejectedArray.forEach(fun=>{
						fun(error);
					})
				}
			}
		apply(onFulfill,onReject);
	}
}

这一部分是最比较的简单,只需要有逆向思维,基本上能明白了;

then 方法的实现

Primise.then 方法接收两个参数,分别是resolve回调方法和reject回调方法,只会执行其中一个方法,then 方法是整个Promise的核心部分,最难理解的部分也是在这里。 then方法接收两个入参,都是回调,一个是resolve,一个是reject方法,根据Promise.status有三种情况:

  1. status === 'Fulfiled' 时,直接调用resolve回调方法,并把回调方法返回结果生成一个新的Promise,返回;
  2. status === 'Rejected' 时,直接调用reject回调方法,并把回调返回结果生成一个新的Pomise,返回;
  3. status === 'Pending'时,我们需要等待Promise的结果,但是因为我们需要返回一个Promise的原因,所以才有了把push 进onFulfilledArray,onRejectedArray数组的这种操作;
// 后面会解释resolvePromise方法的用处;
resolvePromise(promise2 : Promise, result : any, resolve : Function, reject : Function){
	
}
then(onFulfilled : Function, onRejected : Function) : Promise {
        // 如果没有传入,则补充默认的onFulfilled/onRejected方法
	onFulfilled = onFulfilled ? onFulfilled : data => data;
	onRejected = onRejected ? onRejected : error => error;
	let promise2 : Promise;
        // 如果调用时Promise状态是完成了的
	if(this.status === PROMISE_STATUS.FULFIL){
            // 返回一个新的Promise对象
		return promise2 = new Promise((resolve,reject)=>{
			try{
				let result = onFulfilled(this.result);
                          // 这里是一个封装了的处理result的方法
                          // 可以处理 result 是 Promise对象、或者其它数据
                          this.resolvePromise(promise2,result,resolve,reject);
			}catch(e){
				reject(e);
			}
		})
	}
        // 如果调用时Promise状态是rejected
	if(this.status === PROMISE_STATUS.REJECT){
		return promise2 = new Promise((resolve,reject)=>{
			try{
				let result = onRejected(this.error);
				this.resolvePromise(promise2,result,resolve,reject);
			}catch(e){
				reject(e);
			}
		})
	}
        // 如果调用时Promise状态是Pending时,则需要把回调放进onFulfilledArray/onRejectedArray数组中,让Promise resolve/reject时执行数组里面的方法
	if(this.status === PROMISE_STATUS.PENDING){
		return promise2 = new Promise((resolve,reject)=>{
			this.onFulfilledArray.push(result=>{
				try{
					let _result = onFulfilled(result);
					this.resolvePromise(promise2, _result, resolve, reject);
				}catch(e){
					reject(e)
				}
			})

			this.onRejectedArray.push(result=>{
				try{
					let _result = onRejected(result);
					this.resolvePromise(promise2, _result, resolve, reject);
				}catch(e){
					reject(e)
				}
			})
		})
	}
	return this;
}

resolvePromise 方法

resolvePromise 方法是我们封装来处理新生成的Promise 与回调返回结果相关result 或者error的逻辑; result 可能有以下几种情况:

  1. Promise来的,在Promise.then的结果处理;
  2. 直接处理;
resolvePromise(promise2 : Promise, result : any, resolve : Function, reject : Function){
	if(promise2 === result){
		return reject('error');
	}
	if(result instanceof Promise){
		if(result.status === PROMISE_STATUS.PENDING){
			result.then(value=>{
				this.resolvePromise(promise2,value,resolve,reject);
			}, reject)
		}else{
			result.then(resolve,reject);
		}
		return;
	}
	resolve(result);
}

Promise.catch

catch(onReject: Function) : Promise {
	if(this.status === PROMISE_STATUS.FULFIL){
		return this;
	}
	let promise2;
	return promise2 = new Promise((resolve,reject)=>{
		if(this.status === PROMISE_STATUS.REJECT){
			let result = onReject(this.error);
			this.resolvePromise(proimse2,result,resolve,reject);
			return;
		}
		this.then((result)=>{
			this.resolvePromise(promise2,result,resolve,reject);
		},(error)=>{
			this.resolvePromise(promise2,onReject(error),resolve,reject);
		})
	})
}

Promise.reject

	static reject(error){
		return new Promise((resolve,reject)=>{
			reject(error);
		});
	}
	

Promise.resolve

static resolve(result){
		return new Promise((resolve,reject)=>{
			resolve(result);
		});
	}
	

Promise.all

static all(promises: Promise[]): Promise{
		let resultArray = new Array(promises.length);
		return new Promise((resolve,reject)=>{
			let n = 0, maxLength = resultArray.length;
			let setResult = function(item,idx){
				if(idx + 1 > maxLength) reject('cao');
				if(!resultArray[idx]){
					n ++; 
					resultArray[idx] = item;
					if(n == maxLength ){
						resolve(resultArray);
					}
				}
			}
			for(let idx =0; idx < promises.length; idx ++ ){
				let item = promises[idx];
				try{
					if(item instanceof Promise){
						item.then((result)=>{
							setResult(result,idx);
						}, (error)=>{
							reject(error);
						})
					}else{
						setResult(promises[idx],idx);
					}
				}catch(e){
					reject(e);
				}
			}
		})
	}
	

Promise.race

static race(promises : Promise[]) : Promise{
		return new Promise((resolve,reject)=>{
			let flag = false;
			promises.forEach(p => {
				if(flag){ return; }
				if(p instanceof Promise){
					return p.then(result=>{
						if(flag){ return; }
						flag = true;
						resolve(result);
					},error=>{
						if(flag){ return; }
						flag = true;
						reject(error);
					})
				}
				flag = true;
				resolve(p);
			})
		})
	}
        

全部代码Promise

enum PROMISE_STATUS {
	PENDING,
		FULFIL,
		REJECT,
}
class Promise{
	private onFulfilledArray : Function[] = [];
	private onRejectedArray : Function[] = [];
	status : PROMISE_STATUS = PROMISE_STATUS.PENDING;
	result : any = null;
	error : any = null;
	constructor(apply: Function){
		const onFulfill = (result)=>{
			if(this.status === PROMISE_STATUS.PENDING){
				this.status = PROMISE_STATUS.FULFIL;
				this.result = result;
				this.onFulfilledArray.forEach(fun=>{
					fun(result);
				})
			}
		},
			onReject = (error)=>{
				if(this.status === PROMISE_STATUS.PENDING){
					this.status = PROMISE_STATUS.REJECT;
					this.error = error;
					this.onRejectedArray.forEach(fun=>{
						fun(error);
					})
				}
			}
		apply(onFulfill,onReject);
	}
	resolvePromise(promise2 : Promise, result : any, resolve : Function, reject : Function){
		if(promise2 === result){
			return reject('error');
		}
		if(result instanceof Promise){
			if(result.status === PROMISE_STATUS.PENDING){
				result.then(value=>{
					this.resolvePromise(promise2,value,resolve,reject);
				}, reject)
			}else{
				result.then(resolve,reject);
			}
			return;
		}
		resolve(result);
	}
	then(onFulfilled : Function, onRejected : Function) : Promise {
		onFulfilled = onFulfilled ? onFulfilled : data => data;
		onRejected = onRejected ? onRejected : error => error;
		let promise2 : Promise;
		if(this.status === PROMISE_STATUS.FULFIL){
			return promise2 = new Promise((resolve,reject)=>{
				try{
					let result = onFulfilled(this.result);
					this.resolvePromise(promise2,result,resolve,reject);
				}catch(e){
					reject(e);
				}
			})
		}
		if(this.status === PROMISE_STATUS.REJECT){
			return promise2 = new Promise((resolve,reject)=>{
				try{
					let result = onRejected(this.error);
					this.resolvePromise(promise2,result,resolve,reject);
				}catch(e){
					reject(e);
				}
			})
		}
		if(this.status === PROMISE_STATUS.PENDING){
			return promise2 = new Promise((resolve,reject)=>{
				this.onFulfilledArray.push(result=>{
					try{
						let _result = onFulfilled(result);
						this.resolvePromise(promise2, _result, resolve, reject);
					}catch(e){
						reject(e)
					}
				})

				this.onRejectedArray.push(result=>{
					try{
						let _result = onRejected(result);
						this.resolvePromise(promise2, _result, resolve, reject);
					}catch(e){
						reject(e)
					}
				})
			})
		}
		return this;
	}
	catch(onReject: Function) : Promise {
		if(this.status === PROMISE_STATUS.FULFIL){
			return this;
		}
		let promise2;
		return promise2 = new Promise((resolve,reject)=>{
			if(this.status === PROMISE_STATUS.REJECT){
				let result = onReject(this.error);
				this.resolvePromise(proimse2,result,resolve,reject);
				return;
			}
			this.then((result)=>{
				this.resolvePromise(promise2,result,resolve,reject);
			},(error)=>{
				this.resolvePromise(promise2,onReject(error),resolve,reject);
			})
		})
	}
	static reject(error){
		return new Promise((resolve,reject)=>{
			reject(error);
		});
	}
	static resolve(result){
		return new Promise((resolve,reject)=>{
			resolve(result);
		});
	}
	static all(promises: Promise[]): Promise{
		let resultArray = new Array(promises.length);
		return new Promise((resolve,reject)=>{
			let n = 0, maxLength = resultArray.length;
			let setResult = function(item,idx){
				if(idx + 1 > maxLength) reject('cao');
				if(!resultArray[idx]){
					n ++; 
					resultArray[idx] = item;
					if(n == maxLength ){
						resolve(resultArray);
					}
				}
			}
			for(let idx =0; idx < promises.length; idx ++ ){
				let item = promises[idx];
				try{
					if(item instanceof Promise){
						item.then((result)=>{
							setResult(result,idx);
						}, (error)=>{
							reject(error);
						})
					}else{
						setResult(promises[idx],idx);
					}
				}catch(e){
					reject(e);
				}
			}
		})
	}
	static race(promises : Promise[]) : Promise{
		return new Promise((resolve,reject)=>{
			let flag = false;
			promises.forEach(p => {
				if(flag){ return; }
				if(p instanceof Promise){
					return p.then(result=>{
						if(flag){ return; }
						flag = true;
						resolve(result);
					},error=>{
						if(flag){ return; }
						flag = true;
						reject(error);
					})
				}
				flag = true;
				resolve(p);
			})
		})
	}
}