一步一步实现手写promise

2,204 阅读5分钟

promise的优点

promise可以解决js异步回调地狱的问题;

promise能够将业务逻辑与数据处理分隔开使代码更优雅,方便阅读,更有利于代码维护。

第一步:实现promise的简单用法

先看一个promise的基本使用实例:

let p = new Promise((resolve,reject) => {
    resolve('fullFilled')
});

p.then((res) => {
    console.log(res);//fullFilled
})

结合实例分析promise规范我们能够知道:

1,promisepending,fulfilled,rejected三种状态。pending代表等待的状态,在此状态下,fulfilld代表成功态,此状态下执行resolve()方法,rejected代表失败态,此状态下执行reject()方法;一旦状态由pending变为fulfilled或者rejected,则状态将不能再改变;

2,每个promsie都有一个then方法;

3,如果new Promise报错了会走失败态(throw new Error('报错')也会走失败态)。

// 第一步实现如下
class Promise{
    constructor(extrect){
        this.state = 'pending';// 定义promise状态
        this.value = '';//定义成功的返回值
        this.reason = '';//定义失败的返回值
        //定义成功的回调函数
        let resolve = (value) => {
            // 判断是否是pending状态,是则执行,不是则返回
            if(this.state === 'pending'){
                this.state = 'fulFilled';
                this.value = value;
            }
        }
        //定义失败的回调函数
        let reject = (reason) => {
            if(this.state === 'pending'){
                this.state = 'rejected';
                this.reason = reason;
            }
        }
        
        try{
            extrect(resolve,reject)
        }catch(err){
            reject(err)
        }
    }
    // 定义then方法
    then(onFuilled,onRejected){
        if(this.state === 'fulFilled'){
            onFuilled(this.value)
        };
        if(this.state === 'rejected'){
            onRejected(this.reason)
        }
    }
}

let p = new Promise((resolve,reject) => {
    reject('error')
})
p.then((res) => {
    console.log(res);
},(err) => {
    console.log(err);
})

由此promise的简单用法已实现,但是此时,如果使用异步调用,则promise就会卡在``pending`状态,无法继续往下执行;

第二步,实现promise的异步调用

此时我们需要一个发布订阅者模式,在pending状态时将成功的回调函数和失败的回调函数分别放入各自的事件队列中,等状态发生改变时,再调用他们;

首先我们需要两个事件队列:

this.onFuiFilledCallbacks = [];//成功的事件队列
this.onRejectedCallbacks = [];//失败的事件队列

其次在pending状态时分别将成功的回调和失败的回调分别压入各自的事件队列中:

if(this.state === 'pending'){
    this.onFuiFilledCallbacks.push(() => onFuiFilled(this.value));
    this.onRejectedCallbacks.push(() => onRejected(this.reason));
}

最后在状态改变时分别取调用:

//定义成功的回调函数
let resolve = (value) => {
    // 判断是否是pending状态,是则执行,不是则返回
    if(this.state === 'pending'){
        this.state = 'fulFilled';
        this.value = value;
        this.onFuiFilledCallbacks.forEach((fn) => fn());
    }
}
//定义失败的回调函数
let reject = (reason) => {
    if(this.state === 'pending'){
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn());
    }
}

完整代码如下:

class Promise{
    constructor(extrect){
        this.state = 'pending';// 定义promise状态
        this.value = '';//定义成功的返回值
        this.reason = '';//定义失败的返回值
        this.onFuiFilledCallbacks = [];//成功的回调
        this.onRejectedCallbacks = [];//失败的回调
        //定义成功的回调函数
        let resolve = (value) => {
            // 判断是否是pending状态,是则执行,不是则返回
            if(this.state === 'pending'){
                this.state = 'fulFilled';
                this.value = value;
                this.onFuiFilledCallbacks.forEach((fn) => fn());
            }
        }
        //定义失败的回调函数
        let reject = (reason) => {
            if(this.state === 'pending'){
                this.state = 'rejected';
                this.reason = reason;
                this.onRejectedCallbacks.forEach((fn) => fn());
            }
        }
        
        try{
            extrect(resolve,reject)
        }catch(err){
            reject(err)
        }
    }
    // 定义then方法
    then(onFuilled,onRejected){
        if(this.state === 'fulFilled'){
            onFuilled(this.value)
        };
        if(this.state === 'rejected'){
            onRejected(this.reason)
        };
        if(this.state === 'pending'){
            this.onFuiFilledCallbacks.push(() => onFuilled(this.value));
            this.onRejectedCallbacks.push(() => onRejected(this.reason))
        }
    }
}

let p = new Promise((resolve,reject) => {
    setTimeout(() => {
        // resolve('success');
        reject('error')
    },1000)
})
p.then((res) => {
    console.log(res);
},(err) => {
    console.log(err);
})

第三步,实现promise的链式调用

promise的链式调用形如promise.then().then(),每个then返回的也是一个promise;所以我们的then方法也应该返回一个新的promise

// 实例方法
then(onFullFiled, onRejected) {
	let promise2 = new Promise((resolve, reject) => {
		let x;
		if (this.status === 'fullFiled') {
			try {
				x = onFullFiled(this.value);
				// 难点
				// 如果回调函数的结果`x`是一个普通值,那么就`resolve`出去给下一个`then`链式调用,如果是一个`promise`对象则代表又是一个异步,那么调用`x`的`then`方法,将`resolve`和`reject`传进去,等到`x`内部的异步执行完毕(状态完成)就会自动执行传入的`resolve`,这样就实现了`promise`的链式调用。
				x instanceof Promise
					? x.then(resolve, reject)
					: resolve(x);
			} catch (err) {
				reject(err);
			}
		}
		if (this.status === 'rejected') {
			try {
				x = onRejected(this.reason);
				x instanceof Promise
					? x.then(resolve, reject)
					: resolve(x);
			} catch (err) {
				reject(err);
			}
		}
		if (this.status === 'pending') {
			this.onFullFiledCallback.push(() => {
				try {
					x = onFullFiled(this.value);
					x instanceof Promise
						? x.then(resolve, reject)
						: resolve(x);
				} catch (err) {
					reject(err);
				}
			});
			this.onRejectrdCallback.push(() => {
				try {
					x = onRejected(this.reason);
					x instanceof Promise
						? x.then(resolve, reject)
						: resolve(x);
				} catch (err) {
					reject(err);
				}
			});
		}
	});
	return promise2;
}

此时有一个难点需要注意: 如果回调函数的结果x是一个普通值,那么就resolve出去给下一个then链式调用,如果是一个promise对象则代表又是一个异步,那么调用xthen方法,将resolvereject传进去,等到x内部的异步执行完毕(状态完成)就会自动执行传入的resolve,这样就实现了promise的链式调用。

完整代码如下:

class Promise {
    constructor(extrect) {
	    this.status = 'pending'; //promise状态
	    this.value = undefined; //resolve成功结果
	    this.reason = undefined; //reject失败结果
	    this.onFullFiledCallback = []; //成功回调列表
	    this.onRejectrdCallback = []; //失败回调列表
	    // 判断是否是函数
	    this.isFunction = (value) => typeof value === 'function';

    	// 成功回调
        let resolve = (value) => {
            if (this.status === 'pending') {
                this.status = 'fullFiled';
                this.value = value;
                this.onFullFiledCallback.forEach((fn) => fn());
            }
    	};
        // 失败回调
        let reject = (reason) => {
            if (this.status === 'pending') {
                this.status = 'rejected';
                this.reason = reason;
                this.onRejectrdCallback.forEach((fn) => fn());
            }
        };
        // 执行回调
        try {
        	extrect(resolve, reject);
        } catch (err) {
        	reject(err);
        }
    }
    // 实例方法
    then(onFullFiled, onRejected) {
    	onFullFiled = this.isFunction(onFullFiled)
    		? onFullFiled
    		: (data) => data;
    	onRejected = this.isFunction(onRejected)
    		? onRejected
    		: (err) => {
    				throw err;
    		  };
    	let promise2 = new Promise((resolve, reject) => {
    		let x;
    		if (this.status === 'fullFiled') {
                    try {
                    	x = onFullFiled(this.value);
                    	x instanceof Promise ? x.then(resolve, reject) : resolve(x);
                    } catch (err) {
                    	reject(err);
                    }
    		}
    		if (this.status === 'rejected') {
    			try {
    				x = onRejected(this.reason);
    				x instanceof Promise ? x.then(resolve, reject) : resolve(x);
    			} catch (err) {
    				reject(err);
    			}
    		}
    		if (this.status === 'pending') {
    			this.onFullFiledCallback.push(() => {
    				try {
    					x = onFullFiled(this.value);
    					x instanceof Promise
    						? x.then(resolve, reject)
    						: resolve(x);
    				} catch (err) {
    					reject(err);
    				}
    			});
    			this.onRejectrdCallback.push(() => {
    				try {
    					x = onRejected(this.reason);
    					x instanceof Promise
    						? x.then(resolve, reject)
    						: resolve(x);
    				} catch (err) {
    					reject(err);
    				}
    			});
    		}
    	});
    	return promise2;
    }

    catch(onReject) {
        return this.then(undefined, onReject);
    }

    finally(cb) {
        return this.then(
            (value) => Promise.resolve(cb()).then(() => value),
            (reason) =>
                Promise.reject(cb()).then(() => {
            	    throw reason;
            })
        );
    }

    static resolve(value) {
        if (value instanceof Promise) return value;
        return new Promise((resolve) => resolve(value));
    }

    static reject(reason) {
        return new Promise((resolve, reject) => reject(reason));
    }
	
	static all(promiseList) {
		return new Promise((resolve, reject) => {
			let valueArr = [];
			let index = 0;
			for (let i = 0; i < promiseList.length; i++) {
				if (promiseList[i] instanceof Promise) {
					promiseList[i].then(
						(res) => {
							valueArr[index] = res;
							index++;
							if (index === promiseList.length) {
								resolve(valueArr);
							}
						},
						(err) => {
							reject(err);
						}
					);
				} else {
					valueArr[index] = res;
					index++;
					if (index === promiseList.length) {
						resolve(valueArr);
					}
				}
			}
		});
	}
	
    static race(promiseList) {
        return new Promise((resolve, reject) => {
    	    for (let i = 0; i < promiseList.length; i++) {
    		if (promiseList[i] instanceof Promise) {
    			promiseList[i].then(
    				(res) => {
    					resolve(res);
    				},
    				(err) => {
    					reject(err);
    				}
    			);
    		} else {
    			resolve(promiseList[i]);
    		}
    	    }
        });
    }
}

let p = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve('resolve');
		reject('reject');
	}, 500);
});

p.then(
	(res) => {
		console.log(res);
		return new Promise((resolve, reject) => {
			resolve('promise2 success');
			reject('promise2 filed');
		});
	},
	(err) => {
		console.log(err);
		return 'promise2 filed';
	}
)
	.finally(() => {
		console.log('999');
	})
	.then((res) => {
		console.log(res);
	})
	.finally(() => {
		console.log('888');
	});

到此promise核心的代码都已经实现了,后面的静态方法,catch,all,race,resolve,reject,finally就不在赘述。