关于手写promise 的方法

243 阅读6分钟

本人最近在学习 promise 的相关内容与大家分享,若有不对的地方欢迎大家指正,一起学习进步

一、写一个同步的 promise

1、new Promise((resolve,reject)=>{}) 传入的是一个 executor ,且参数传入后就同步执行
2、executor 有两个参数,resolve/reject且都可以执行,所以都是函数

function Promise (executor){  // 构造对象
    // 初始化参数
    this.status = 'pending';
    this.value = null;
    this.reason = null;
    // 定义 resolve 
    const resolve = value =>{
        if(this.status === 'pending'){
            this.status = 'rsolved';
            this.value = value;
        }
    }
    // 定义 reject 
    const reject = reason => {
        if(this.status === 'pending'){
            this.status = 'rejected';
            this.reason =reason;
        }
    }
    // 立即执行
    executor(resolve,reject);
}

// promise 的 then 方法
Promise.prototype.then = function(onFulfilled,onRejected){
    if(this.status === 'resolved'){
        onFulfilled(this.value);
    }
    if(this.status === 'rejected'){
        onRejected(reason);
    }
}


掌握这些内容就可以初出江湖了

二、解决异步调用问题

1、当 executor 中存在 setTimeout 等方法时会产生异步调用,此时我们需要将onFulfilled()/onRejected(),放到一个数组中等 executor 中的内容出结果后再进行调用
2、then 后定义的内容是异步执行的,需要对其中的内容使用process.nextTick() 方法进行处理

function Promise (executor){
    this.status = 'pending';
    this.value = null;
    this.reason = null;
    // 添加数组当 executor 中异步时,保存函数
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = value =>{
        if(this.status === 'pending'){
            this.status = 'resolved';
            this.value = value;
            // 当 executor 出结果时调用函数
            this.onFulfilledCallbacks.map(callback=>{
                callback(this.value);
            });
        }
    }
    const reject = reason => {
        if(this.status === 'pending'){
            this.status = 'rejected';
            this.reason =reason;
            // 当 executor 出结果时调用函数
            this.onRejectedCallbacks.map(callback=>{
                callback(this.reason);
            });
        }
    }
    executor(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
    // 添加 process.nextTick() 增加异步
    if(this.status === 'resolved'){
        process.nextTick(()=>{
            onFulfilled(this.value);
        })
    }
    if(this.status === 'rejected'){
        process.nextTick(()=>{
            onRejected(this.reason);
        })
    }
    // 在 status 是 pending 状态下向数组添加属性
    if(this.status === 'pending'){
        this.onFulfilledCallbacks.push(
            process.nextTick(()=>{
                onFulfilled(this.value);
            })
        )
        this.onRejectedCallbacks.push(
            process.nextTick(()=>{
                onRejected(this.reason);
            })
        )
    }
}


还没完,重头戏刚刚开始

三、解决链式调用

我们常用的 new Promise().then().then() 就是链式调用,常用来解决回调地狱
链式调用中分三种情况

1、then() 的参数中没有定义 onFulfilled/onRejected 时
2、then() 的参数中,返回的不是一个 promise 或无返回值时
3、then() 的参数中返回一个 promise

对应这三种情况 then 返回的 promise (这里称之为 P2)也存在三种不同的状态,反别是

1、将上一层的 promise 的 data 传给 P2
2、将这非 promise 的内容作为 data 传给 P2
3、将参数中返回的 promise 的 data 传给 P2

所以现在要构建一个 P2 且要将上述的三种情况串在一起,根据它们的状态判断 P2 的状态

上代码

function Promise (executor){
    this.status = 'pending';
    this.value = null;
    this.reason = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = value =>{
        if(this.status === 'pending'){
            this.status = 'resolved';
            this.value = value;
            this.onFulfilledCallbacks.map(callback=>{
                callback(this.value);
            });
        }
    }
    const reject = reason => {
        if(this.status === 'pending'){
            this.status = 'rejected';
            this.reason =reason;
            this.onRejectedCallbacks.map(callback=>{
                callback(this.reason);
            });
        }
    }
    executor(resolve,reject);
}
Promise.prototype.then = function(onFulfilled,onRejected){
    // case1: 如果未定义 onFulfilled/onRejected 自动初始化
    if(typeof onFulfilled !== 'function'){
        onFulfilled = value => value;
    }
    if(typeof onRejected !== 'function'){
        onRejected = err => {throw err;}
    }
    // case2:/case3: 定义了 onFulfilled/onRejected 
    // P2 的状态由 then 中的参数状态决定
    // 将 then 中的参数表达式命名为 P3
    // 所以(P3)then 中的参数表达式必须写在P2中(只有在P2中才能看到P2的 resolve/reject)
    let p2 = new Promise((resolve, reject)=>{
        if(this.status === 'resolved'){
            process.nextTick(()=>{
                try {
                    let P3 = onFulfilled(this.value);
                    // 建立 P2 与 P3 的联系
                    resolvePromiseRelation(p2,P3,resolve,reject);
                } catch (e) {
                    reject(e);
                }
                
            })
        }
        if(this.status === 'rejected'){
            process.nextTick(()=>{
                try {
                    let P3 = onRejected(this.reason);
                    // 建立 P2 与 P3 的联系
                    resolvePromiseRelation(p2,P3,resolve,reject);
                } catch (e) {
                    reject(e);
                }
                
            })
        }
        if(this.status === 'pending'){
            this.onFulfilledCallbacks.push(() =>{
                process.nextTick(()=>{
                    try {
                        let P3 = onFulfilled(this.value);
                        // 建立 P2 与 P3 的联系
                        	resolvePromiseRelation(p2,P3,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            })
            this.onRejectedCallbacks.push(()=>{
                process.nextTick(()=>{
                    try {
                        let P3 = onRejected(this.reason);
                        // 建立 P2 与 P3 的联系
                        resolvePromiseRelation(p2,P3,resolve,reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            })
        }
    });
    return p2;
}
// 建立 P2 与 P3 的连接
function resolvePromiseRelation(P2,P3,P2Resolve,P2Reject){
    // 如果 P2 === P3 会陷入死循环
    if(P2 === P3){
        return P2Reject(new TypeError('Dead cycle P2 = P3'));
    };
    let called = false;
    // 判断 P3 是个什么东西
    if(P3 !== null && (typeof P3 === 'function' || typeof P3 === 'object')){
        try {
            let then = P3.then;
            if(typeof then === 'function'){
                 // 说明 P3 是一个 promise 对象
                then.call(P3, data => {
                    // 运行一次之后本次递归不能再来运行
                    if(called) return;
                     called = true;
                     // 递归保证 data 中不是一个 promise 对象
                     resolvePromiseRelation(P3,data,P2Resolve,P2Reject);
                }, err => {
                    // 当出错后不再进行递归
                    if(called) return;
                     called = true;
                     P2Reject(err);
                });
            }else{
                // case2: 返回的不是 promise 对象
                P2Resolve(P3);
            };
        } catch (e) {
            // 当出错后不再进行递归
            if(called) return;
            called = true;
            P2Reject(e);
        }
        
    }else{
        // case2: 返回的不是 promise 对象
        P2Resolve(P3);
    };
}


还没结束,继续ing...
...
...
此时产生一个问题,若 resolve 的参数是一个 promise 对象时,程序状态不会根据此对象状态改变,会将这个 promise 对象内容返回,这不是我们希望看到的!
此时我们需要对进入进入 resolve 的东西进行区分,若进入的是 promise 对象需要对其不断进行递归,直到进入的不再是 promise 对象

代码更改后状态

function Promise (executor){
    this.status = 'pending';
    this.value = null;
    this.reason = null;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    // 由于 resolve 存在递归所以需要 called 监视状态
    this.called = false;

    const _resolve = value =>{
        this.status = 'resolved';
        this.value = value;
        this.onFulfilledCallbacks.map(callback=>{
            callback(this.value);
        });
    }
    const reject = reason => {
        if(this.status === 'pending'&& !this.called ){
            this.caiied = true;
            this.status = 'rejected';
            this.reason = reason;
            this.onRejectedCallbacks.map(callback=>{
                callback(this.reason);
            });
        }
    }
    // resolve 给入的有可能是一个 promise 对象,若是 promise 需要进行处理
    const resolve = value =>{
        // 进入时检测 called 的状态
        if(this.status === 'pending' && !this.called ){
            this.caiied = true;

            // called 防止出错后递归不停止
            let called = false;
            if(value !== null && (typeof value === 'function' || typeof value === 'object')){
                try {
                    let then = value.then;
                    if(typeof then === 'function'){
                        then.call(value, data => {
                            if(called) return;
                            called = true;
                            // 递归前恢复 this.called 的状态防止无法递归
                            this.called = false;
                            // data 还可能是 promise 对象,所以再进行递归
                            resolve(data);
                        },err => {
                            if(called) return;
                            called = true;
                            // 递归前恢复 this.called 的状态防止无法递归
                            this.called = false;
                            reject(err);
                        });
                    }else{
                        _resolve(value);
                    };
                } catch(e) {
                    if(called) return;
                    called = true;
                    // 递归前恢复 this.called 的状态防止无法递归
                    this.called = false;
                    reject(e);
                }
            }else{
                _resolve(value);
            };
        }
    }

    executor(resolve,reject);
}

终于 promise 代码写完了,紧接着就是 promise 的成员函数和方法....


四、promise 的成员函数即方法

写之前首先要清楚 promise 都有哪些成员函数和方法
1、挂在 promise 的 protoytpe 上的
catch()/finally()
2、挂在 promise 上的
all() 、race()、resolve()、reject()

由于以上已经将 promise 的主要部分写完,这直接写成员方法
catch() 和 finally()

// catch() 和 finally()
Promise.prototype.catch = function(onRejected){
	return this.then(null,onRejected);
}
Promise.prototype.finally = function(fn){
	return this.then(() => {
		fn();
	},() => {
		fn();
	})
}

all() 、race()、resolve()、reject()方法

//all() 、race()、resolve()、reject()
Promise.all = function(ps){
	return new Promise((resolve,reject) => {
		let x = new Map();
		function yy(key,promise){
			x.set(key,promise);
			if(x.size === ps.length){
				resolve(x);
			}
		}
		
		for(let i=0;i<ps.length;i++){
			let p = ps[i];
			if(p && p.then && typeof p.then === 'function'){
				p.then(() => {
					yy(i,p);
				},reject)
			}else{
				yy(i,p);
			}
		}
	})
}

Promise.race = function(ps){
	return new Promise((resolve,reject) => {
		for(var i=0;i<ps.length;i++){
			let p = ps[i];
			if(p && p.then && typeof p.then === 'funtion'){
				p.then(() => {
					resolve(p);
				},reject)
			}else{
				resolve(p);
			}
		}
	})
}

Promise.resolve = function(k){
	return new Promise((resolve,reject) => {
		resolve(k);
	})
}

Promise.reject = function(k){
	return new Promise((resolve,reject) => {
		reject(k);
	})
}

除此之位 promise 上还需要实现一个 defer 的对象接口,实现方法如下:

Promise.defer = Promise.deferred = function(){
	let dfd = {};
	dfd.promise = new Promise((resolve,reject) => {
		dfd.resolve = resolve;
		dfd.reject = reject;
	})
	return dfd;
}

如果手写的代码想要给别人使用需要创造一个接口:

module.exports = Promise;

测试代码:
使用 cmd 可以对手写的代码进行测试看是否存在不足的地方 测试方法如下:
在 cmd 中 cnpm install -g promises-aplus-tests 安装测试包
安装完成后 :输入 promises-aplus-tests +文件名
会给出测试结果



至此手写 promise 的全部过程已经全部结束
写这篇文章是为了对自己所学的东西进行一次梳理,在以后可以时长看看回顾一下,同时将自己学的东西与大家进行分享,自己学习过程中经历了无数BUG真的让人崩溃,希望我的文章能对你有一定的帮助,我也会感到高兴...