基本框架
要想手写Promise,我们就要先了解Promise到底实现了什么,先从最基础的地方开始。
Promise最基本的地方就是传入一个函数形式的参数,在then方法传入对于该函数所对应的成功或者失败的回调函数
let p = new Promise((resolve, reject) => {
resolve('OK')
})
p.then(
value => {
console.log(value)
},reason => {
console.log(reason)
})
因此,我们手写需要在自制的Promise函数内部添加原型方法then,在constructor构造器函数内部添加需要传入以函数作为参数的executor变量
class Promise {
constructor(executor) {
}
//添加then方法
then(onResolve, onReject) {
}
}
同时,then方法也需要接收两个以函数作为参数的变量,我们分别命名为 onResolve
和 onReject
resolve和reject方法构建
Promise中说明了执行器函数即executor在内部是同步调用的,因此我们直接在constructor中对executor进行调用
constructor(executor) {
executor();
}
在调用的时候,我们发现官方Promise函数的执行器函数会传入两个参数 resolve 和 reject,因此,我们需要在executor中传入这两个参数,但是光传入还不行,我们还需要对这两个参数进行声明,我们通过观察官方的Promise函数内部resolve和reject的调用可以发现,这两个参数明显是两个函数类型的数据,且该函数还接受了一个参数,所以我们需要以函数形式声明resolve和reject:
constructor(executor) {
const resolve = function(data){};
const reject = function(data){};
executor(resolve,reject);
}
resolve和reject代码实现
我们研究resolve函数可以发现它主要做到两个事情:
-
在调用时修改Promise实例对象的状态为
fulfill
。fulfill 和 resolved 都表示成功的状态
-
在调用时修改Promise实例对象的结果为它本身接收的参数。
说到实例对象的状态和结果,打印过Promise实例对象的人都知道,状态和结果是该实例对象上的两个属性
因此,我们在Promise身上也设置两个属性 PromiseState
和 PrmiseResult
,并且让resolve在调用时修改这两个属性的值。
constructor(executor) {
this.PromiseState = 'pending'
this.PrmiseResult = null
//resolve函数
const resolve = (data) => {
//1.修改状态(promiseState)
this.PromiseState = 'fulfilled'
//2.设置结果值(promiseResult)
this.PrmiseResult = data
}
//reject函数同理
······
executor(resolve,reject);
}
注意由于this指向问题,我们将resolve和reject的函数从匿名函数改为箭头函数。
reject的写法相同,只是将状态改为rejected即可。
状态只允许修改一次
官方的Promise在使用中我们还能发现,已经修改过Promise状态的实例对象无法再次修改状态
let p = new Promise((resolve, reject) => {
resolve('OK');
reject('BAD')
})
如上述代码所示,最后实例对象返回状态为 fulfill,返回值为 OK,下面的reject函数不会执行。
因此我们在resolve中插入if判断,从而保证只修改一次状态
const resolve = (data) => {
//1.修改状态(promiseState)
if (this.PromiseState != 'pending') return // 保证只修改一次
this.PromiseState = 'fulfilled'
//2.设置结果值(promiseResult)
this.PrmiseResult = data
}
}
throw抛出改变状态
官方的Promise除了resolve和reject能改变状态以外,throw的抛出也能改变Promise实例对象的状态,因此我们也需要加上该功能:
constructor(executor) {
this.PromiseState = 'pending'
this.PrmiseResult = null
//resolve实现函数
·······
//reject实现函数
·······
//捕获throw抛出的错误
try {
executor(resolve, reject)
} catch (error) {
reject(error)//调用reject将状态改为失败
}
}
then方法回调使用
官方的Promise.then方法中会传入两个参数,成功调用第一个参数,而失败调用第二个参数,因此我们在Promise中使用if来实现该功能:
//添加then方法
then(onResolve, onReject) {
if (this.PromiseState == 'fulfilled') {
onResolve(this.PrmiseResult);
}
if (this.PromiseState == 'rejected') {
onReject(this.PrmiseResult);
}
}
同样的,我们除了使用
PromiseState
判断以外,还要将结果PrmiseResult
传入以供函数调用。
异步任务执行
我们使用的Promise很多情况下执行的都是异步任务,比如http请求处理,文件的调用,最简单的也有定时器的执行之类的异步任务,而要求是即使是异步任务,Promise也会等待任务完成之后再返回结果和状态改变。
//添加then方法
then(onResolve, onReject) {
if (this.PromiseState == 'fulfilled') {
onResolve(this.PrmiseResult);
}
if (this.PromiseState == 'rejected') {
onReject(this.PrmiseResult);
}
//判断pending状态,如果此时状态还为pending则交给resolve和reject,在这两个函数调用的时候调用回调
if (this.PromiseState == 'pending') {
//·······
}
}
而进入pending的if中后,我们究竟应该做些什么呢,我们知道无论是onResolve
还是onReject
,都需要在状态改变后调用,也就是在 resolve 或者reject 函数调用后调用,而这两个函数并不是在同一个作用域内,我们无法直接在resolve中调用 onResolve
,因此,我们在pending的if中该做的就是保存回调函数。
同样,回调函数保存的位置我们保存在Promise 中,这样比较安全。
class Promise {
constructor(executor) {
this.PromiseState = 'pending'
this.PrmiseResult = null
this.callback = {} //用于存储回调函数
//resolve函数
······
//reject函数
······
//捕获throw跑出的错误
······
}
//添加then方法
then(onResolve, onReject) {
if (this.PromiseState == 'fulfilled') {
onResolve(this.PrmiseResult);
}
if (this.PromiseState == 'rejected') {
onReject(this.PrmiseResult);
}
//判断pending状态,如果此时状态还为pending则交给resolve和reject,在这两个函数调用的时候调用回调
if (this.PromiseState == 'pending') {
//保存回调函数
this.callback = {
onResolve,
onReject //这里使用了es6的键值同名,省略键的操作
}
}
}
}
保存完之后,我们需要在resolve或者reject中进行调用
const resolve = (data) => {
//1.修改状态(promiseState)
if (this.PromiseState != 'pending') return // 保证只修改一次
this.PromiseState = 'fulfilled'
//2.设置结果值(promiseResult)
this.PrmiseResult = data
//执行回调
if(this.callback.onResolve){
this.callback.onResolve(data)
}
}
reject函数同理,就不多赘述
实现多个回调
我们都知道,当为同一个Promise实例对象指定多个回调的时候,只要状态发生改变,多个回调全都会执行,演示如下:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK')
}, 2000)
})
p.then(value=>{
console.log('111',value)
})
p.then(value=>{
console.log('222',value)
})
而我们现在的callback只会保存最后一次的回调函数,前面的回调会全部被覆盖掉,因此将callback定为对象是有所欠缺的,因此,我们将callback改为数组,每当被指定回调时就将回调压入数组,这样所有回调都可以被保存。
class Promise {
constructor(executor) {
this.PromiseState = 'pending'
this.PrmiseResult = null
this.callback = [] //用于存储回调函数
//resolve函数
······
//reject函数
······
//捕获throw跑出的错误
······
}
//添加then方法
then(onResolve, onReject) {
if (this.PromiseState == 'fulfilled') {
onResolve(this.PrmiseResult);
}
if (this.PromiseState == 'rejected') {
onReject(this.PrmiseResult);
}
//判断pending状态,如果此时状态还为pending则交给resolve和reject,在这两个函数调用的时候调用回调
if (this.PromiseState == 'pending') {
//保存回调函数
this.callback.push = ({
onResolve,
onReject //这里使用了es6的键值同名,省略键的操作
})
}
}
}
同样,resolve和reject函数也需要修改
const resolve = (data) => {
//1.修改状态(promiseState)
if (this.PromiseState != 'pending') return // 保证只修改一次
this.PromiseState = 'fulfilled'
//2.设置结果值(promiseResult)
this.PrmiseResult = data
//执行回调
//使用数组和遍历来保证多次then回调都能执行
if (this.callback.length != 0) {
this.callback.forEach(x => {
x.onResolve(data)
})
}
}
then方法的return结果返回
我们都知道,then方法返回的结果是个Promise对象,该对象成功与否通过内部onResolve
或者onReject
函数的return来决定,如果return返回的是非Promise实例对象,那么then返回成功的结果,如果返回的是Promise实例对象,那么then将根据Promise实例对象返回的结果成功与否来决定自身成功与否。
根据要求,首先我们要将then方法的返回结果改为Promise对象
then(onResolve, onReject) {
//then方法将返回一个Promise对象
return new Promise((resolve, reject) => {
if (this.PromiseState == 'fulfilled') {
onResolve(this.PrmiseResult);
}
if (this.PromiseState == 'rejected') {
onReject(this.PrmiseResult);
}
//判断pending状态,如果此时状态还为pending则交给resolve和reject,在这两个函数调用的时候调用回调
if (this.PromiseState == 'pending') {
//保存回调函数
this.callback.push = ({
onResolve,
onReject //这里使用了es6的键值同名,省略键的操作
})
}
})
}
接下来就是最关键的一步,由于then方法返回的Promise对象受到内部函数onResolve
和onReject
return的影响,我们就要获取onResolve
或者onReject
的return结果然后进行判断
try {
if (this.PromiseState == 'fulfilled') {
//让then返回的结果给result,判断return的结果是否为Promise对象
let result = onResolve(this.PrmiseResult);
//是Promise对象,则执行then方法,根据result的Promise对象是否成功来决定这个Promise对象是否成功
if (result instanceof Promise) {
result.then(
response => {
resolve(response)
},
error => {
reject(error)
}
)
}
else {
//非Promise对象,直接给成功
resolve(result)
}
}
} catch (error) {
reject(error)
}
由于
onResolve
函数内部有可能出现抛出异常的可能,当抛出异常时也会导致当前then返回的Promise对象的状态为失败,因此我们需要使用trycatch
来捕获异常并调用reject函数。
if判断rejected和上述代码片段同理
当然,我们目前修改了同步任务下的then方法返回Promise对象的状态改变,而异步任务并未改变:
if (this.PromiseState == 'pending') {
//保存回调函数
this.callback.push = ({
onResolve,
onReject //这里使用了es6的键值同名,省略键的操作
})
}
此时如果是异步任务,由于没有调用resolve和reject两个中的任意一个函数,导致then方法return的Promise对象的状态还是pending,不符合我们的要求,因此,我们要对该处代码进行改变。
if (this.PromiseState == 'pending') {
//保存回调函数
this.callback.push = ({
onResolve:()=>{
try{
//让then返回的结果给result,判断return的结果是否为Promise对象
let result = onResolve(this.PrmiseResult);
//是Promise对象,则执行then方法,根据result的Promise对象是否成功来决定这个Promise对象是否成功
if (result instanceof Promise) {
result.then(
response => {
resolve(response)
},
error => {
reject(error)
}
)
}
else {
//非Promise对象,直接给成功
resolve(result)
}
} catch (error) {
reject(error)
}
},
onReject:()=>{
······
}
})
}
onReject
的修改与onResolve
同理
异常穿透与值传递
这里就比较简单了,我们只需要在then方法上判断onResolve
和onReject
是否是函数,如果不是函数补上函数即可。
then(onResolve, onReject) {
//异常穿透原理
if (typeof onReject !== 'function') {
onReject = reason => {
throw reason
}
}
if (typeof onResolve !== 'function') {
onResolve = value => value
}
//then方法将返回一个Promise对象
return new Promise((resolve, reject) => {
······
})
小结
手写Promise函数的主体和主要思想目前都已完成,而剩下的几个方法的封装也比较简单,就不一一赘述,Promise函数主要难点在于函数之间相互嵌套,需要我们能够掌握每个参数之间的关系,每个函数调用的位置,如果能够理清思路那么就会发现Promise并没有想象中那么难。
以上是我学习手写Promise的一些理解和笔记,希望对你能有帮助
标签:resolve,函数,PromiseState,详解,onResolve,reject,Promise,手写 From: https://www.cnblogs.com/sakura-hfhj/p/16659538.html