[手写系列]带你实现一个简单的Promise

博客 动态
0 228
优雅殿下
优雅殿下 2022-03-08 10:56:16
悬赏:0 积分 收藏

[手写系列] 带你实现一个简单的Promise

简介

学习之前 需要先对Promise有个基本了解哦,这里都默认大家都是比较熟悉Promise的

本次将带小伙伴们实现Promise的基本功能

  1. Promise的基本骨架
  2. Promisethen
  3. Promise.then的多次调用
  4. then链式调用
  5. catch的实现
  6. finally的实现

01-搭建基本骨架

const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";class ZXPromise {    constructor(executor) {        this.status = PROMISE_STATUS_PENDING;        const resolve = (value) => {            if (this.status === PROMISE_STATUS_PENDING) {                this.status = PROMISE_STATUS_FULFILLED;                console.log(value);            }        }        const rejected = (reason) => {            if (this.status === PROMISE_STATUS_PENDING) {                this.status = PROMISE_STATUS_REJECTED;                console.log(reason);            }        }        executor(resolve, rejected)    }}// 初步搭建好Promise的construtor结构const promise = new ZXPromise((resolve, rejected) => {    resolve("123");    rejected("wushichu")})
  • 因为Promise有三种状态pending,fulfilled,rejected,我们这里就声明三个常量来代表这三种状态
  • Promise中需要传递一个回调函数,他的参数中包含了resolverejected,调用resolve之后,状态会变为fulfilled,调用rejected,状态会变成rejected
  • 我定义了一个类,我们在constructor中定义所需要的resolverejected函数,然后将这两个函数传入那个executor中去,这样Promise的基本骨架就已经搭建完成了,非常简单.

02-实现Promise的then功能

const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";class ZXPromise {	constructor(executor) {		this.status = PROMISE_STATUS_PENDING;		const resolve = (value) => {			if (this.status === PROMISE_STATUS_PENDING) {				queueMicrotask(() => {          //因为只有pending状态才能进行变化          if(this.status!==PROMISE_STATUS_PENDING) return          this.status = PROMISE_STATUS_FULFILLED;					if (this.onfufilled)						this.onfufilled(value);				})			}		}		const rejected = (reason) => {			if (this.status === PROMISE_STATUS_PENDING) {				queueMicrotask(() => {          if(this.status!==PROMISE_STATUS_PENDING) return          this.status = PROMISE_STATUS_REJECTED;					if (this.onrejected)						this.onrejected(reason);				})			}		}		executor(resolve, rejected)	}	then(onfufilled, onrejected) {		this.onfufilled = onfufilled;		this.onrejected = onrejected;	}}// 接下来开始写then方法const promise = new ZXPromise((resolve, rejected) => {	resolve("123");	rejected("wushichu");})promise.then((res) => {	console.log("res", res);}, (err) => {	console.log("err", err);})
  • then方法中接受两个参数,分别是onfulfilledonrejected两个函数,分别对应着状态fulfilledrejected
  • 这里要注意一个点我在resolverejected中都使用了queueMicrotask,这里使用的目的是为了保证顺序执行的一致性,确保在then方法执行过后,再去执行相关代码,这里需要大家熟悉微任务队列和宏任务队列,推荐大家看下这篇文章

03-Promise.then多次调用

大家可以用上一部分的代码实验一下,如果多次调用,会发现只有最后一个输出,并且在定时器中使用,会出现结果为undefined

p1.then((res) => {	console.log("res1", res);});p1.then((res) => {	console.log('res2: ', res);});setTimeout(() => {	p1.then((res) => {		console.log("res4", res);	})}, 1000);

现在我们来解决下上述问题,看代码

const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";class ZXPromise {	constructor(executor) {		this.status = PROMISE_STATUS_PENDING;		this.value = undefined;		this.reason = undefined;		this.onfufilled = [];		this.onrejected = [];		const resolve = (value) => {			if (this.status === PROMISE_STATUS_PENDING) {				queueMicrotask(() => {					if (this.status !== PROMISE_STATUS_PENDING) return					this.status = PROMISE_STATUS_FULFILLED;					this.value = value;					this.onfufilled.forEach(fn => {						fn(value);					});				})			}		}		const rejected = (reason) => {			if (this.status === PROMISE_STATUS_PENDING) {				queueMicrotask(() => {					if (this.status !== PROMISE_STATUS_PENDING) return					this.status = PROMISE_STATUS_REJECTED;					this.reason = reason;					this.onrejected.forEach(fn => {						fn(reason);					})				})			}		}		executor(resolve, rejected)	}	// 接下来为了Promise能够多次调用 进行优化	then(onfufilled, onrejected) {		if (this.status === PROMISE_STATUS_FULFILLED) {			onfufilled(this.value);		}		if (this.status === PROMISE_STATUS_REJECTED) {			onrejected(this.value);		}		if (this.status === PROMISE_STATUS_PENDING) {			this.onfufilled.push(onfufilled);			this.onrejected.push(onrejected);		}	}}
  • 因为改进之后,需要存储resolverejectedvaluereason值,所以我们定义了这两个值
  • 为了满足多次调用,我们需要将promise中的onfulfilledonrejected改为数组存储以用来满足我们的多次调用
  • 定时器的问题我这边说下,因为setTimeout属于宏任务,在同步代码执行完毕之后,会接着执行微任务,所以宏任务是最后来执行的,所以也就造成了promise中的代码执行完了,但是包裹在定时器中的then方法没有获取到结果
  • 所以呢,在这里我决定让处于定时器中的代码直接执行而不压入数组中去,因为定时器之前的代码已经执行完毕了,promise的状态也已经发生了改变,所以我就在then方法中判断promise的状态,如果是fulfilledrejected状态的话,传过来的函数就直接执行

04-then方法的链式调用

要想实现链式调用,那么then方法肯定是将Promise对象又给返回出来了,说到这了大家有没有思路呢?

const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";class ZXPromise {	constructor(executor) {		this.status = PROMISE_STATUS_PENDING;		this.value = undefined;		this.reason = undefined;		this.onfufilled = [];		this.onrejected = [];		const resolve = (value) => {			if (this.status === PROMISE_STATUS_PENDING) {				queueMicrotask(() => {					if (this.status !== PROMISE_STATUS_PENDING) return					this.status = PROMISE_STATUS_FULFILLED;					this.value = value;					this.onfufilled.forEach(fn => {						fn(value);					});				})			}		}		const rejected = (reason) => {			if (this.status === PROMISE_STATUS_PENDING) {				queueMicrotask(() => {					if (this.status !== PROMISE_STATUS_PENDING) return					this.status = PROMISE_STATUS_REJECTED;					this.reason = reason;					this.onrejected.forEach(fn => {						fn(reason);					})				})			}		}		try{			executor(resolve, rejected)		}catch(err){			console.log(err);		}			}		then(onfufilled, onrejected) {		return new ZXPromise((resolve, rejected) => {			if (this.status === PROMISE_STATUS_FULFILLED) {				try {					//如果then中有返回值,就会作为下一个then所接收的值					const value = onfufilled(this.value);					resolve(value);				} catch (err) {					rejected(err);				}							}			if (this.status === PROMISE_STATUS_REJECTED) {				try {					const value = onrejected(this.value);					resolve(value);				} catch (err) {					rejected(err);				}			}			if (this.status === PROMISE_STATUS_PENDING) {				try {					this.onfufilled.push(() => {						const value = onfufilled(this.value);						resolve(value);					});				} catch (err) {					rejected(err);				}				try {					this.onrejected.push(() => {						const value = onrejected(this.value);						resolve(value);					});				} catch (err) {					rejected(err);				}			}		})	}}const promise = new ZXPromise((resolve, rejected) => {	resolve("123");	rejected("wushichu");})promise.then((res) => {	console.log("res1:", res);	return "abc";}, (err) => {	console.log("err1", err);}).then((res) => {	console.log("res2", res);}, (err) => {	console.log("err2", err);})
  • 变化最大的就是then方法了,大家可以看到我又把ZXPromise返回出去了,代码中我写的很清楚了

05-catch方法实现

catch方法实际上是then第二个参数的语法糖,说到这里大家有没有明白什么呢?

const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";const execFnWithCatchError = (execFn, value, resolve, reject) => {    try {        const result = execFn(value);        resolve(result);    } catch (err) {        reject(err);    }}class ZXPromise {    constructor(executor) {        this.status = PROMISE_STATUS_PENDING;        this.value = undefined;        this.reason = undefined;        this.onfufilled = [];        this.onrejected = [];        const resolve = (value) => {            if (this.status === PROMISE_STATUS_PENDING) {                queueMicrotask(() => {                    if (this.status !== PROMISE_STATUS_PENDING) return                    this.status = PROMISE_STATUS_FULFILLED;                    this.value = value;                    this.onfufilled.forEach(fn => {                        fn(value);                    });                })            }        }        const rejected = (reason) => {            if (this.status === PROMISE_STATUS_PENDING) {                queueMicrotask(() => {                    if (this.status !== PROMISE_STATUS_PENDING) return                    this.status = PROMISE_STATUS_REJECTED;                    this.reason = reason;                    this.onrejected.forEach(fn => {                        fn(reason);                    })                    return this.reason;                })            }        }        executor(resolve, rejected)    }    then(onfufilled, onrejected) {        //这一段是为了将错误代码传递下去的        const defaultOnRejected = err => { throw err }        onrejected = onrejected || defaultOnRejected        return new ZXPromise((resolve, rejected) => {            if (this.status === PROMISE_STATUS_FULFILLED && onfufilled) {                execFnWithCatchError(onfufilled, this.value, resolve, rejected);            }            if (this.status === PROMISE_STATUS_REJECTED && onrejected) {                execFnWithCatchError(onrejected, this.reason, resolve, rejected);            }            if (this.status === PROMISE_STATUS_PENDING) {                if (onfufilled)                    this.onfufilled.push(() => {                        execFnWithCatchError(onfufilled, this.value, resolve, rejected);                    });                if (onrejected) {                    this.onrejected.push(() => {                        execFnWithCatchError(onrejected, this.reason, resolve, rejected);                    });                }            }        })    }    catch(onrejected) {        return this.then(undefined, onrejected);    }}
  • 大家可以看到catch代码实际上就只有一行,就是将then方法进行了调用,是不是相当简单呢
  • 然后我觉得那个try catch代码重复性比较高,所以我将它提取了出来复用
  • 然后大家看下那个then里面的开头,onrejected函数被给予了一个默认值,如果then没有传递第二个参数,那么会被赋予一个错误处理函数的默认值,抛出错误后,会自动被try catch捕获进行reject,这样子错误会被层层传递,一直到最后被catch函数所执行.

06-finally的实现

finally就是要在最后执行的函数,无论什么情况,实现起来也是非常简单

    finally(fn) {        return this.then(() => { fn() }, () => { fn() });    }
  • 在类中加上这一段代码就好了,因为finally是无法接收任何resolve和rejected的值的,所以我们在传递的函数中执行fn,就是避免resolve的值和rejected的值被传递到finally上去

07-完整代码总览

const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";const execFnWithCatchError = (execFn, value, resolve, reject) => {    try {        const result = execFn(value);        resolve(result);    } catch (err) {        reject(err);    }}class ZXPromise {    constructor(executor) {        this.status = PROMISE_STATUS_PENDING;        this.value = undefined;        this.reason = undefined;        this.onfufilled = [];        this.onrejected = [];        const resolve = (value) => {            if (this.status === PROMISE_STATUS_PENDING) {                queueMicrotask(() => {                    if (this.status !== PROMISE_STATUS_PENDING) return                    this.status = PROMISE_STATUS_FULFILLED;                    this.value = value;                    this.onfufilled.forEach(fn => {                        fn(value);                    });                })            }        }        const rejected = (reason) => {            if (this.status === PROMISE_STATUS_PENDING) {                queueMicrotask(() => {                    if (this.status !== PROMISE_STATUS_PENDING) return                    this.status = PROMISE_STATUS_REJECTED;                    this.reason = reason;                    this.onrejected.forEach(fn => {                        fn(reason);                    })                    return this.reason;                })            }        }        executor(resolve, rejected)    }    then(onfufilled, onrejected) {        //这一段是为了将错误代码传递下去的        const defaultOnRejected = err => { throw err }        onrejected = onrejected || defaultOnRejected        return new ZXPromise((resolve, rejected) => {            if (this.status === PROMISE_STATUS_FULFILLED && onfufilled) {                execFnWithCatchError(onfufilled, this.value, resolve, rejected);            }            if (this.status === PROMISE_STATUS_REJECTED && onrejected) {                execFnWithCatchError(onrejected, this.reason, resolve, rejected);            }            if (this.status === PROMISE_STATUS_PENDING) {                if (onfufilled)                    this.onfufilled.push(() => {                        execFnWithCatchError(onfufilled, this.value, resolve, rejected);                    });                if (onrejected) {                    this.onrejected.push(() => {                        execFnWithCatchError(onrejected, this.reason, resolve, rejected);                    });                }            }        })    }    catch(onrejected) {        return this.then(undefined, onrejected);    }    finally(fn) {        return this.then(() => { fn() }, () => { fn() });    }}
  • 大家可以自行进行测试
posted @ 2022-03-08 10:42 CodeSpirit 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员