Promise类核心逻辑实现解析

一、Promise简单分析

Promise是一个类,在执行这个类的时候需要传递一个执行器进去,执行器会立即执行

  1. Promise中有三个状态:pending fulfilled rejected。并且状态只能由 pending --> fulfilledpending --> rejected。一旦状态确定,就不可更改。
  2. resolve函数和reject函数是用来更改状态的, resolve 对应 fulfilled状态   reject 对应 rejected状态。
  3. then方法内部做的事情就是判断状态,如果状态成功就执行成功回调,如果状态失败,就执行失败回调,then方法是被定义在原型对象中的。
  • then成功回调有一个参数,表示成功之后的值,失败回调有一个参数,表示失败的原因
  • then方法是可以被链式调用的,后面的then方法的回调函数拿到值是上一个then方法的回调函数的返回值(要实现then的链式调用,那么then需要返回一个promise对象)
  • 在then方法里面可以返回一个普通值,也可以是一个promise对象:
  1. 普通值:直接调用resolve方法,将这个普通值给传递出去
  2. promise对象:先去查看返回的promise对象的状态,如果是成功的,调用resolve将这个成功的状态传给下一个promise对象,如果是失败的,调用reject将这个失败的结果传给下一个promise对象

        4. 捕获异常:增加try/catch

  • executor执行器函数报错捕获异常
  • 当前then回调函数执行过程中报错了,我们要在下一个then错误回调中捕获到
/**
一、判断当前then回调的的执行结果 x 的值是普通对象还是Promise对象
     1. 如果是普通值,直接调用resolve
     2. 如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject
  
二、根据传参判断 promise2 和 x 是否相等,识别Promise自返回的问题,比如这样:
     const promise = new Promise((resolve, reject) => {
        resolve(100);
    	})
    	var p1 = promise.then(res => {
        console.log(res);
        return p1;
    	})
      控制台会报错【TypeError: Chaining cycle detected for promise #<Promise>】
*/

function resolvePromise(promise2, x, resolve, reject) {
    if (x instanceof MyPromise) {
        x.then(resolve, reject)
      // x.then(value => resolve(value), reason => reject(reason))
    } else {
        resolve(x);
    }
}

二、Promise源码简单实现

1. 自实现Promise

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
    // 构造函数接收执行器函数
    constructor(executor) {
        // executor执行器捕获异常
        try {
            executor(this.resolve, this.reject);
        } catch (error) {
            this.reject(error);
        }
    }
    // 声明实例属性
    status = PENDING; // 状态
    value = undefined; // 成功值
    reason = undefined; // 失败原因
    successCallbacks = []; // 成功回调
    failCallbacks = []; // 失败回调
    // 箭头函数的作用:让resolve/reject函数内部的this指向这个myPromise的实例对象
    resolve = value => {
        // 因为状态一旦改变不可更改,只能是 PENDING -> FULFILLED 所以设置:如果状态不是PENDING,阻止程序向下执行
        if(this.status !== PENDING) return;
        // 将状态更改为成功
        this.status = FULFILLED;
        // 保存成功之后的值
        this.value = value;
        // 判断成功回调是否存在,如果存在就调用
        while(this.successCallbacks.length) this.successCallbacks.shift()();
    }
    reject = error => {
         // 如果状态不是PENDING,阻止程序向下执行
         if(this.status !== PENDING) return;
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败的原因
        this.reason = error;
        // 判断失败回调是否存在,如果存在就调用
        while(this.failCallbacks.length) this.failCallbacks.shift()();
    }
    // 要实现then的链式调用,需要在then里面返回一个Promise
    then = (onFulfilled, onRejected) => {
        // 实现假如中间某几个then没有传递参数,则把value/reason传递下去
        onFulfilled = onFulfilled ? onFulfilled : value => value;
        onRejected = onRejected ? onRejected : reason => {throw reason};
        let promise2 = new MyPromise((resolve, reject) => {
            // 判断状态
            if (this.status === FULFILLED) {
                // setTimeout为了把这一段代码变成异步,因为promise2这个直接直接获取会获取不到,当这一段代码执行完毕才有promise2,异步代码会在所有的同步代码执行完毕再执行,这个时候就有promise2了
                setTimeout(() => {
                    // 捕获then执行过程中报错
                    try {
                        let x = onFulfilled(this.value); // 当前then成功回调的执行结果
                        /*
                        判断当前then回调的的执行结果 x 的值是普通对象还是Promise对象
                        如果是普通值,直接调用resolve
                        如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject
                        */
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0)
            } else if(this.status === REJECTED) {
                setTimeout(() => {
                    // 捕获then执行过程中报错
                    try {
                        let x = onRejected(this.reason); // 当前then失败回调的执行结果
                        /*
                        判断当前then回调的的执行结果 x 的值是普通对象还是Promise对象
                        如果是普通值,直接调用resolve
                        如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject
                        */
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0)
            } else {
                // 等待,将成功回调和失败回调保存下来
                this.successCallbacks.push(() => {
                    setTimeout(() => {
                        // 捕获then执行过程中报错
                        try {
                            let x = onFulfilled(this.value); // 当前then成功回调的执行结果
                            /*
                            判断当前then回调的的执行结果 x 的值是普通对象还是Promise对象
                            如果是普通值,直接调用resolve
                            如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject
                            */
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0)
                });
                this.failCallbacks.push(() => {
                    setTimeout(() => {
                        // 捕获then执行过程中报错
                        try {
                            let x = onRejected(this.reason); // 当前then失败回调的执行结果
                            /*
                            判断当前then回调的的执行结果 x 的值是普通对象还是Promise对象
                            如果是普通值,直接调用resolve
                            如果是Promise对象,查看Promise对象返回的状态,根据状态决定调用resolve还是reject
                            */
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0)
                });
            }
        });
        return promise2;
    }
    // 处理当前状态为失败的回调
    catch(onRejected) {
        return this.then(undefined, onRejected);
    }
}

接下来让我们测试一下我们代码实现是否有问题:

// 测试then方法返回一个promise对象的情况
promise.then(res => {
    console.log(res)
    return other()
}).then(res => console.log(res));


// 测试then回调返回自己的情况
let p1 = promise.then(value => {
    console.log('value =', value);
    return p1;
});
p1.then(res => {
    console.log(res)
}, reason => {
    console.log('reason =', reason.message) // reason = Chaining cycle detected for promise #<MyPromise>
});


// 测试执行器错误
let p3 = new MyPromise((resolve, reject) => {
    throw new Error('executor error!!!');
})
p3.then(value => {
    console.log('p3 =', value);
}, err => {
    console.log('p3 =', err.message); // p3 = executor error!!!
})


// 测试then回调函数执行过程中报错,在下一个then的错误回调中捕获到
let p4 = new MyPromise((resolve, reject) => {
    resolve('123');
})
p4.then(value => {
    console.log('p4-succ-1 =', value); // p4-succ-1 = 123
    throw new Error('then error!!!');
}, err => {
    console.log('p4-err-1 =', err.message);
}).then(value => {
    console.log('p4-succ-2 =', value);
}, err => {
    console.log('p4-err-2 =', err.message); // 4-err-2 = then error!!!
})


// 测试then失败链式调用
let p5 = new MyPromise((resolve, reject) => {
    reject('失败啦,啦啦啦!!');
})
p5.then(value => {
    console.log('p5-succ-1 =', value);
}, err => {
    console.log('p5-err-1 =', err); // p5-err-1 = 失败啦,啦啦啦!!
    return 10000
}).then(res => {
    console.log('p5-succ-2 =', res); // p5-succ-2 = 10000
}, err => {
    console.log('p5-err-2 =', err);
});


// 测试异步情况的捕获异常和then链式调用
let p6 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('2s后,成功了,啦啦啦!!');
    }, 2000)
})
p6.then(value => {
    console.log('p6-succ-1 =', value); // p6-succ-1 = 2s后,成功了,啦啦啦!!
    return 'aaa';
}, err => {
    console.log('p6-err-1 =', err);
    return 10000
}).then(res => {
    console.log('p6-succ-2 =', res); // p6-succ-2 = aaa
}, err => {
    console.log('p6-err-2 =', err);
});


// 测试then链式调用中间几个then方法不传递任何参数
let p7 = new MyPromise((resolve, reject) => {
    resolve(10000);
})
p7.then().then().then(res => {
    console.log('p7 =', res); // p7 = 10000
}, err => {
    console.log('p7 =', err);
});

let p8 = new MyPromise((resolve, reject) => {
    reject(new Error('p8 Error!!'));
})
p8.then().then().then(res => {
    console.log('p8 =', res);
}, err => {
    console.log('p8 =', err.message); // p8 = p8 Error!!
});

2. 自实现Promise.all()

  • 作用:


用来解决异步并发的问题,允许我们按照异步代码调用的顺序,得到异步代码执行的结果。

  1. 参数为一个数组:元素可以为普通值 或者 Promise对象
  2. 返回值:Promise对象,所以可以链式调用then方法
  3. 如果所有的Promise状态都是成功的,那么最终结果是成功的,如果有一个结果是失败的,那么结果就是失败的。
  • 实现原理:

1. 接收一个数组作为参数,返回一个promise对象。首先声明一个result数组,用来放我们最终的结果。

2. 通过for循环传递进来的数组,判断数组元素是普通值还是Promise对象:

  • 如果是普通值,通过调用addData方法给放到result数组中
  • 如果是promise对象,执行这个promise对象,执行完后看状态,如果是成功的,则把成功的值通过addData添加到result数组中,如果是失败状态,则直接调用reject,让最终的状态为失败的就行

但是目前思路有一点问题:

for循环是一下子就执行完的,如果里面遇到异步的代码我们就要等待所有的异步操作执行完毕在调用resolve方法,这个时候声明一个index,每执行一次addData,index就加一,在addData里面判断index的值是否和传参数组的长度一样,如果一样就调用resolve方法,把result传递出去

class MyPromise {
  	// 声明静态方法
	static all (array) {
    	let result = [];
    	let index = 0; // 判断当数组中的元素是否都执行完毕
    	return new MyPromise((resolve, reject) => {
        	function addData(key, value) {
            	result[key] = value;
            	index++;
                // 都执行完毕,再向外传值result
            	if (index === array.length) {
                	resolve(result);
            	}
        	}
          	// 遍历传进来的数组,判断元素是普通值还是Promise对象
        	for(let i = 0; i < array.length; i++) {
            	let current = array[i];
              	// 如果是Promise对象,执行完毕获取结果添加到result数组中
            	if (current instanceof MyPromise) {
                	current.then(res => {
                    	addData(i, res);
                	}, err => {
                    	reject(err); // 如果执行失败,直接reject向外传失败原因
                	})
            	} else {
                	// 如果是普通对象,直接添加到result中
                	addData(i, array[i]);
            	}
        	}
    	});
	}
}

// 测试
function p9() {
    return new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve('p9');
        }, 2000)
    })
}
function p10() {
    return new MyPromise((resolve, reject) => {
        resolve('p10');
    })
}
// 调用Promise.all静态方法
MyPromise.all(['a', 'b', p9(), p10(), 'c']).then(res => {
    console.log(res); // [ 'a', 'b', 'p9', 'p10', 'c' ]
}, err => {
    console.log('error =', err)
})

3. 自实现Promise.race()

// 哪个执行快输出哪个,无论是成功状态还是失败状态
class MyPromise {
	static race(arr) {
        return new MyPromise((resolve, reject) => {
            for (let i=0; i<arr.length; i++) {
                let cur = arr[i];
                if (cur instanceof MyPromise) {
                    cur.then(resolve, reject);
                } else {
                    resolve(cur);
                }
            }
        })
    }
}

4. 自实现Promise.allSettled()

class MyPromise {
	static allSettled(arr) {
        return new MyPromise((resolve, reject) => {
            const data = [];
            const len = arr.length;
            let count = len;
            for (let i = 0; i < len; i++) {
                const cur = arr[i];
                cur.then(res => {
                    data[i] = { status: 'fulfilled', value: res };
                }, error => {
                    data[i] = { status: 'rejected', reason: error };
                }).finally(() => {
                    if (!--count) {
                        resolve(data);
                    }
                });
            }
        });
    }
}

5. 自实现Promise.resolve()

作用:

是将给定的值转换为promise对象(返回值就是一个promise对象,其中包裹着给定的值)

可以接收一个普通值或者一个Promise对象:

  • 普通值:返回一个resolved状态 value为普通值的Promise对象
  • Promise对象:会原封不动的作为Promise.resolve() 方法的返回值,调用then方法拿到这个传参promise对象的返回值

实现原理:

判断传入的是不是一个Promise对象:

  • 如果是,那么原封不动的把这个promise对象返回就行了
  • 如果不是,那么就创建一个promise对象,把给定的值包裹在promise对象中,然后再返回这个promise对象就行了
class MyPromise {
	static resolve(value) {
		if (value instanceof MyPromise) return value;
		return new MyPromise(resolve => resolve(value));
	}
}	
MyPromise.resolve(10).then(val => console.log(val)); // 10
MyPromise.resolve(p9()).then(res => console.log(res)); // p9

6. 自实现new Promise().finally()

它不是一个静态方法,是原型上的方法,接收一个回调函数作为参数
特点:

  1. 无论当前promise对象的最终的状态是成功的还是失败的,finally()方法都会执行一次
  2. 在finally()后面可以链式调用then方法来拿到这个Promise对象最终返回的结果

如果finally()中返回了一个promise对象,里面有一个定时器,那么需要等待这个promise执行完毕再去执行finally的then回调,这个时候就需要使用MyPromise中的静态方法resolve()

  • 如果finally中callback执行返回是一个普通值,转换成promise对象执行完成
  • 如果返回是一个promise对象还等待执行完成,然后给返回。
class MyPromise {
	finally(callback) {
		return this.then(value => {
			return MyPromise.resolve(callback()).then(() => value);
		}, err => {
			return MyPromise.resolve(callback()).then(() => {throw err});
		})
	}
}
// 测试
function p11() {
	return new MyPromise((resolve, reject) => {
		resolve('p11 resolved');
	})
}
p11().finally(() => {
	console.log('finally'); // finally
	return p9();
}).then(
    res => console.log(res),
    err => console.log(err)
); // p11 resolved

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值