当前位置: 首页 > java >正文

使用class手搓Promise,三步一回头

理解本文的实现逻辑需要掌握Promise及其使用方法。

搭建初始结构

这里我们采用类搭建Promise的初始结构。我们创建Promise对象时,一般写法let p = new Promise((resolve,reject)=>{resolve(222)})。我们输出p看一下。
在这里插入图片描述
我们首先在构造函数中声明并执行创建对象时的(resolve,reject)=>{resolve(222)}这个回调函数。

  class CustomPromise {constructor(initFunc) {initFunc()}}

这个时候如果我们创建CustomPromise的实例对象会报错。因为resolve,reject两个入参我们没有声明,在执行到resolve(222)时报错。下面进行声明:

  class CustomPromise {constructor(initFunc) {initFunc(this.resolve, this.reject)}resolve(value) {}reject(value) { }}

这个时候我们执行let cp = new CustomPromise((resolve, reject) => { resolve(222) })这段代码看一下
在这里插入图片描述
已经不报错了,非常的棒。但是是没有任何属性的空对象。我们把Promise的三个状态:等待状态pending、成功状态fulfilled、失败状态rejected和值声明出来。

class CustomPromise {constructor(initFunc) {this.status = 'pending'this.result = undefinedinitFunc(this.resolve, this.reject)}resolve(value) {}reject(value) {}
}

在这里插入图片描述
好,基本架构搭建完成。

搭建resolve和reject方法

PromiseA+规范中说明,Promise状态只能通过pending转为fulfilled或rejected。我们需要在resolve和reject函数中增加此逻辑。这两个函数主要功能是改变Promise的状态和设置Promise的值。我们来实现:

class CustomPromise {constructor(initFunc) {this.status = 'pending'this.result = undefinedinitFunc(this.resolve.bind(this), this.reject.bind(this))}resolve(value) {if (this.status !== 'pending') returnthis.status = 'fulfilled'this.result = value}reject(value) {if (this.status !== 'pending') returnthis.status = 'rejected'this.result = value}
}

因为initFunc函数内参数是回调函数,所以resolve和reject函数只能手动声明this,我们通过bind手动绑定。这里不能通过call进行绑定,因为resolve函数时用户调用的。
在这里插入图片描述
这里在初始化对象时,执行的回调函数(CustomPromise中的initFunc执行时要处理异常情况,所以需要用try...catch处理)
至此,初始结构丰满完成,已经初具人形了。

搭建then方法

我们平时使用Promise时:

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

大概像这样,会输出:(这段Promise的逻辑输出后面文章也会输出,代码就不放了。)
在这里插入图片描述
所以我们看到Promise的then函数会实现的基础功能为:

  • 接收两个回调函数
  • 根据p不同的状态调用不同的函数
class CustomPromise {constructor(initFunc) {this.status = 'pending'this.result = undefinedtry {initFunc(this.resolve.bind(this), this.reject.bind(this))} catch (error) {this.reject(error)}}resolve(value) {if (this.status !== 'pending') returnthis.status = 'fulfilled'this.result = value}reject(value) {if (this.status !== 'pending') returnthis.status = 'rejected'this.result = value}then(onFulfilled, onRejected) {if (this.status === 'fulfilled') {onFulfilled(this.result)}if (this.status === 'rejected') {onRejected(this.result)}}
}

这样就实现了then的基本逻辑。

兼容异步任务

上面我们封装CustomPromise都是基于CustomPromise同步创建,这样initFunc函数(初始回调函数)>resolve函数(改变状态和值)>then函数可以同步执行,如果异步创建呢?我们执行这段程序。

let cp = new CustomPromise((resolve, reject) => {setTimeout(() => {resolve(222)}, 1000);
})console.log(cp)
cp.then(res => {console.log(res, '1')
})

在这里插入图片描述
我们看到输出的cp的状态为pending,then函数的回调函数没有执行。

为啥子呢?一开始创建cp时,status初始状态就是pending,reault值为undefined。这个时候resolve在宏任务中,被排到了下一次事件循环。接着就是执行同步任务,输出了cp,即为初始状态。接着是执行then函数,此时status的状态还是pending,在我们目前搭建的CustomPromise中,并没有对此状态的处理。

因此,增加此逻辑处理,把此状态下then的回调函数保存起来,等待resolve或reject函数内容执行完成后再执行。

class CustomPromise {constructor(initFunc) {this.status = 'pending'this.result = undefinedthis.onFulfilledFunc = undefinedthis.onRejectedFunc = undefinedtry {initFunc(this.resolve.bind(this), this.reject.bind(this))} catch (error) {this.reject(error)}}resolve(value) {if (this.status !== 'pending') returnthis.status = 'fulfilled'this.result = valuethis.onFulfilledFunc && this.onFulfilledFunc(this.result)}reject(value) {if (this.status !== 'pending') returnthis.status = 'rejected'this.result = valuethis.onRejectedFunc && this.onRejectedFunc(this.result)}then(onFulfilled, onRejected) {if (this.status === 'fulfilled') {onFulfilled(this.result)}if (this.status === 'rejected') {onRejected(this.result)}if (this.status === 'pending') {this.onFulfilledFunc = onFulfilledthis.onRejectedFunc = onRejected}}
}

在这里插入图片描述
这里我们看到上面程序中的输出也在最后输出了出来。所以如果onFulfilledFunc或onRejectedFunc有值的话,就说明创建CustomPromise时,是异步创建。此逻辑的处理相当于延后then函数中回调函数的执行。

另外,PromiseA+规范中说明,

  • then函数内参数必须为函数类型
  • then函数支持多次调用
  • then函数应该返回Promise

then函数内参数必须为函数类型的要求我们在then函数中判断一下即可。then函数支持多次调用这个要求我们看一下:

let cp = new CustomPromise((resolve, reject) => {// setTimeout(() => {resolve(222)// }, 1000);
})
cp.then(res => {console.log(res, 'cp-res')
})
cp.then(res => {console.log(res, '1')
})

在这里插入图片描述
同步创建对象,执行时没啥问题。如果把setTimeout函数注释取消呢?
在这里插入图片描述
Promise那段逻辑输出的pp-res在前,1这个在后了,顺序正确。但是cp-res的输出内容消失了!

(1)创建CustomPromise,状态为pending、值为undefined,
(2)执行第一个then函数,判断状态为pending,保存回调函数至onFulfilledFunc
(3)执行第二个then函数,判断状态为pending,替换回调函数onFulfilledFunc!
(4)setTimeout函数到主线程执行,执行完成后执行替换后的onFulfilledFunc函数。
所以,为了支持多次调用,我们需要把保存的变量修改为数组

class CustomPromise {constructor(initFunc) {this.status = 'pending'this.result = undefinedthis.onFulfilledFuncList = []this.onRejectedFuncList = []try {initFunc(this.resolve.bind(this), this.reject.bind(this))} catch (error) {this.reject(error)}}resolve(value) {if (this.status !== 'pending') returnthis.status = 'fulfilled'this.result = valueif (this.onFulfilledFuncList.length > 0) {this.onFulfilledFuncList.forEach(func => func(this.result))}}reject(value) {if (this.status !== 'pending') returnthis.status = 'rejected'this.result = valueif (this.onRejectedFuncList.length > 0) {this.onRejectedFuncList.forEach(func => func(this.result))}}then(onFulfilled, onRejected) {// 判断入参是否为函数let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => valuelet realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }if (this.status === 'fulfilled') {realOnFulfilled(this.result)}if (this.status === 'rejected') {realOnRejected(this.result)}// 支持多次调用then函数if (this.status === 'pending') {this.onFulfilledFuncList.push(onFulfilled)this.onRejectedFuncList.push(onRejected)}}
}

在这里插入图片描述
如此,这般。

then函数返回Promise

同步情况

因为then函数执行时,回调函数可能返回

  • CustomPromise
  • 原始数据类型或引用数据类型
  • 异常
  • 无回调函数

我们依次看怎么处理

  • CustomPromise
    直接返回即可
  • 原始数据类型或引用数据类型
    通过CustomPromise返回,值为返回值,状态为fulfilled(resolve)
  • 异常
    通过CustomPromise返回,值为异常值,状态为rejected(reject)
  • 无回调函数
    通过CustomPromise返回,状态和值继承调用then函数的CustomPromise对象(相当于没走then函数,直接返回)
then(onFulfilled, onRejected) {// 判断入参是否为函数let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => valuelet realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }return new CustomPromise((resolve, reject) => {if (this.status === 'fulfilled') {try {// 回调函数的值let value = realOnFulfilled(this.result)if (value instanceof CustomPromise) {// 执行后调用回调函数,到最外层返回value.then(res => resolve(res), err => reject(err))} else {resolve(value)}} catch (error) {// 处理异常reject(error)}}if (this.status === 'rejected') {try {// 回调函数的值let value = realOnRejected(this.result)if (value instanceof CustomPromise) {// 执行后调用回调函数,到最外层返回value.then(res => resolve(res), err => reject(err))} else {resolve(value)}} catch (error) {// 处理异常reject(error)}}// 支持多次调用then函数if (this.status === 'pending') {this.onFulfilledFuncList.push(onFulfilled)this.onRejectedFuncList.push(onRejected)}})}

执行此段逻辑

let cp = new CustomPromise((resolve, reject) => {resolve(222)
})
let cp2 = cp.then(res => {console.log(res, 'cp-res')
})
console.log(cp2, 'cp2')

在这里插入图片描述
(1)这里then的回调函数没有返回,其实是按undefined处理(原始数据类型)
(2)如果then返回CustomPromise类型,执行value.then(res => resolve(res), err => reject(err))此时value即为CustomPromise类型。目前处理是执行value对象的then方法,通过第五行统一封装返回。

ps.也可以每种情况单独返回,不通过第五行统一返回。那么第一种情况"then的回调函数没有返回"这么写return new Promise(resolve=>resolve(value)),如果是CustomPromise类型,直接返回return value 。异常捕获这么写return new Promise((resolve,reject)=>reject(value))
(3)异常通过trycatch捕获
(4)无回调函数
此时判断then函数是否为函数的第三、四行为空时的默认函数,就是返回的调用then函数的CustomPromise对象的值。

异步情况

异步和同步的逻辑相同,处理方式略有不同

then(onFulfilled, onRejected) {// 判断入参是否为函数let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => valuelet realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }return new CustomPromise((resolve, reject) => {if (this.status === 'fulfilled') {try {let value = realOnFulfilled(this.result)if (value instanceof CustomPromise) {// 执行后调用回调函数,到最外层返回value.then(res => resolve(res), err => reject(err))} else {resolve(value)}} catch (error) {// 处理异常reject(error)}}if (this.status === 'rejected') {try {let value = realOnRejected(this.result)if (value instanceof CustomPromise) {// 执行后调用回调函数,到最外层返回value.then(res => resolve(res), err => reject(err))} else {resolve(value)}} catch (error) {// 处理异常reject(error)}}// 支持多次调用then函数if (this.status === 'pending') {this.onFulfilledFuncList.push((result) => {try {let value = realOnFulfilled(result)if (value instanceof CustomPromise) {// 执行后调用回调函数,到最外层返回value.then(res => resolve(res), err => reject(err))} else {resolve(value)}} catch (error) {// 处理异常reject(error)}})this.onRejectedFuncList.push((result) => {try {let value = realOnRejected(result)if (value instanceof CustomPromise) {// 执行后调用回调函数,到最外层返回value.then(res => resolve(res), err => reject(err))} else {resolve(value)}} catch (error) {// 处理异常reject(error)}})}})}

这里把then函数的回调函数封装起来,执行后进行返回。result作为入参,是在resolve或reject函数执行时,传入的参数。

方法封装

现在我们把返回CustomPromise这段公共代码封装起来

then(onFulfilled, onRejected) {// 判断入参是否为函数let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => valuelet realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }return new CustomPromise((resolve, reject) => {if (this.status === 'fulfilled') {try {let value = realOnFulfilled(this.result)this.handleThenReturnPromise(value, resolve, reject)} catch (error) {// 处理异常reject(error)}}if (this.status === 'rejected') {try {let value = realOnRejected(this.result)this.handleThenReturnPromise(value, resolve, reject)} catch (error) {// 处理异常reject(error)}}// 支持多次调用then函数if (this.status === 'pending') {this.onFulfilledFuncList.push((result) => {try {let value = realOnFulfilled(result)this.handleThenReturnPromise(value, resolve, reject)} catch (error) {// 处理异常reject(error)}})this.onRejectedFuncList.push((result) => {try {let value = realOnRejected(result)this.handleThenReturnPromise(value, resolve, reject)} catch (error) {// 处理异常reject(error)}})}})
}handleThenReturnPromise(value, resolve, reject) {if (value instanceof CustomPromise) {// 执行后调用回调函数,到最外层返回value.then(res => resolve(res), err => reject(err))} else {resolve(value)}
}

网上有很多方法直接封装成resolvePromise方法,处理了一些公共业务,我们只针对CustomPromise这一种情况,再加上对then函数返回的是嵌套CustomPromise的递归逻辑处理。

handleThenReturnPromise(value, resolve, reject) {if (value instanceof CustomPromise) {// 执行后调用回调函数,到最外层返回value.then(res => this.handleThenReturnPromise(res, resolve, reject), err => reject(err))} else {resolve(value)}
}

微任务

PromiseA+规范规定onFulfilled和onRejected应该是微任务。Promise中then方法的回调函数输出逻辑也是如此:

let p = new Promise((resolve, reject) => { resolve(222) })
let pp = p.then(res => {console.log(res, 'res')
})
console.log(pp, 'pp')

这段逻辑输出如下:
在这里插入图片描述
先执行同步任务(第五行),再执行微任务(then的回调函数)
目前CustomPromise中then的回调函数时同步任务,我们看下执行

let cp = new CustomPromise((resolve, reject) => {resolve(123)console.log("after")
})
let cp2 = cp.then((res) => {console.log(res, 'res')},(error) => {console.log(error, 'error')}
)
console.log('end')

执行结果:
在这里插入图片描述

所以我们使用queueMicrotask把then的回调函数置为微任务。

class CustomPromise {constructor(initFunc) {this.status = 'pending'this.result = undefinedthis.onFulfilledFuncList = []this.onRejectedFuncList = []try {initFunc(this.resolve.bind(this), this.reject.bind(this))} catch (error) {this.reject(error)}}resolve(value) {if (this.status !== 'pending') returnthis.status = 'fulfilled'this.result = valueif (this.onFulfilledFuncList.length > 0) {queueMicrotask(() => {this.onFulfilledFuncList.forEach(func => func(this.result))})}}reject(value) {if (this.status !== 'pending') returnthis.status = 'rejected'this.result = valueif (this.onRejectedFuncList.length > 0) {queueMicrotask(() => {this.onRejectedFuncList.forEach(func => func(this.result))})}}then(onFulfilled, onRejected) {// 判断入参是否为函数let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => valuelet realOnRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }return new CustomPromise((resolve, reject) => {if (this.status === 'fulfilled') {queueMicrotask(() => {try {let value = realOnFulfilled(this.result)this.handleThenReturnPromise(value, resolve, reject)} catch (error) {// 处理异常reject(error)}})}if (this.status === 'rejected') {queueMicrotask(() => {try {let value = realOnRejected(this.result)this.handleThenReturnPromise(value, resolve, reject)} catch (error) {// 处理异常reject(error)}})}// 支持多次调用then函数if (this.status === 'pending') {this.onFulfilledFuncList.push((result) => {try {let value = realOnFulfilled(result)this.handleThenReturnPromise(value, resolve, reject)} catch (error) {// 处理异常reject(error)}})this.onRejectedFuncList.push((result) => {try {let value = realOnRejected(result)this.handleThenReturnPromise(value, resolve, reject)} catch (error) {// 处理异常reject(error)}})}})}handleThenReturnPromise(value, resolve, reject) {if (value instanceof CustomPromise) {// 执行后调用回调函数,到最外层返回value.then(res => this.handleThenReturnPromise(res, resolve, reject), err => reject(err))} else {resolve(value)}}catch(onRejected) {return this.then(null, onRejected)}
}

在then函数的回调函数执行时添加到微任务队列。
在这里插入图片描述

搭建catch方法

我们可以通过调用then函数,把catch的回调函数作为第二个参数传入。

catch(onRejected) {return this.then(null, onRejected)
}

测试一下:

let cp = new CustomPromise((resolve, reject) => {reject(123)
})
cp.catch(err => {console.log(err, 'err')
})

在这里插入图片描述

(1)创建CustomPromise对象后,cp为值为123,状态为rejected的对象;
(2)执行catch方法,走then方法中status==="rejected"的逻辑返回resolve(123)

let cp = new CustomPromise((resolve, reject) => {reject(123)
})
cp.then(res => {console.log(res, 'res')}).catch(err => {console.log(err, 'err')})

这段代码相较于上段代码增加了一段then。两段代码的执行结果相同。
(1)创建CustomPromise对象后,cp为值为123,状态为rejected的对象;
(2)执行then函数,判断状态status==="rejected",但是入参中第二个参数onRejected参数为空,赋值为默认方法error => { throw error },抛出异常后被try…catch捕获,返回CustomPromise对象,状态为reject,值为123。
(3)执行catch方法,走then方法中status==="rejected"的逻辑返回resolve(123)

搭建CustomPromise.resolve()和CustomPromise.reject()方法

类似Promise.resolve()和Promise.reject()方法,可以直接通过类名调用,返回Promise。

static resolve(value) {if (value instanceof CustomPromise) {return value} else {return new CustomPromise(resolve => resolve(value))}
}static reject(value) {return new CustomPromise((resolve, reject) => reject(value))
}

声明静态方法,可以直接调用。

搭建finally方法

finally方法它是保证在Promise调用链的哪一节都会执行并且向后传递调用它的Promise对象。

finally(cb) {// 返回CustomPromisereturn this.then(// 如果调用finally方法的CustomPromise状态为fulfilled,调用此方法(value) => {// 首先保证finally方法中的回调函数执行完成// 返回调用finally方法的CustomPromise的值给后续链使用return CustomPromise.resolve(cb()).then(() => value)},// 如果调用finally方法的CustomPromise状态为rejected,调用此方法(value) => {return CustomPromise.resolve(cb()).then(() => { throw value })})
}

最后还有一些方法像all、race等方法就不多介绍了,如果感兴趣可以点这里看一下,非常详细。

参考:https://juejin.cn/post/7480797795785687040#heading-21
https://zhuanlan.zhihu.com/p/183801144
https://zhuanlan.zhihu.com/p/451254515

http://www.xdnf.cn/news/5919.html

相关文章:

  • 传输层协议UDP
  • latex公式格式
  • Spark自定义分区器-基础
  • 力扣-1.两数之和
  • java的switch case
  • Flutter小白入门指南
  • Spark Streaming 内部运行机制详解
  • MySQL 深度分页怎么优化?
  • 基于 Spring Boot 瑞吉外卖系统开发(十三)
  • SpringBoot中的拦截器
  • 精简大语言模型:用于定制语言模型的自适应知识蒸馏
  • RobotxR1:通过闭环强化学习在大语言模型上实现具身机器人智能
  • 【Web应用】Vue 项目前端项目文件夹和文件介绍
  • 17.责任链模式:思考与解读
  • Mysql索引优化
  • State(状态)——对象行为型模式
  • 在scala中sparkSQL读入csv文件
  • 【AI提示词】贝叶斯分析专家
  • C语言编程--二叉树--构建解析树
  • iOS - 如何从appStore获取app版本信息
  • 各类芒果(果实、叶片、产量等)相关数据集
  • Python爬虫实战:研究JavaScript 环境补全逆向解密
  • SQLMesh信号机制详解:如何精准控制模型评估时机
  • CSS可以继承的样式汇总
  • 【言语】刷题3
  • 串口模块详细讲解
  • IO、存储、硬盘、文件系统相关常识
  • 【Bluedroid】蓝牙 HID DEVICE 初始化流程源码解析
  • 十天学会嵌入式技术之51单片机—day-9
  • 【技巧】使用UV创建python项目的开发环境