一步一步手搓Promise,图文详细讲解,包清晰的

122 阅读11分钟

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

搭建初始结构

这里我们采用“类”搭建Promise的初始结构。我们创建Promise对象时,一般写法let p = new Promise((resolve,reject)=>{resolve(222)})。我们输出p看一下。

image.png

我们首先在构造函数中声明并执行创建对象时的(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) })这段代码看一下:

image.png

已经不报错了,非常的棒。但是此时的cp是没有任何属性的空对象。我们把Promise的三个状态:等待状态pending、成功状态fulfilled、失败状态rejected和值声明出来。

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

    resolve(value) {}

    reject(value) {}
}

我们看一下目前cp打印出来的值: 在这里插入图片描述

目前,基本架构搭建完成。

搭建resolve和reject方法

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

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

    resolve(value) {
        if (this.status !== 'pending') return
        this.status = 'fulfilled'
        this.result = value
    }

    reject(value) {
        if (this.status !== 'pending') return
        this.status = 'rejected'
        this.result = value
    }
}

因为initFunc函数内参数是回调函数,所以resolve和reject函数只能手动声明this,我们通过bind手动绑定。这里不能通过call进行绑定,因为resolve函数是用户调用的(当执行resolve(222)时,才执行此函数)。目前cp的打印出来的值为:

在这里插入图片描述

这里在初始化对象时,执行的回调函数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的逻辑输出后面文章也会输出,代码后面就不放了。) image.png 所以我们看到Promise的then函数会实现的基础功能为:

  • 接收两个回调函数
  • 根据p不同的状态调用不同的函数
class CustomPromise {
    constructor(initFunc) {
        this.status = 'pending'
        this.result = undefined
        try {
            initFunc(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
        }
    }

    resolve(value) {
        if (this.status !== 'pending') return
        this.status = 'fulfilled'
        this.result = value
    }

    reject(value) {
        if (this.status !== 'pending') return
        this.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')
})

let p = new Promise((resolve, reject) => { resolve(222) })

let pp = p
    .then(
        res => {
            console.log(res, 'pp-res')
        },
        err => {
            console.log(err, 'pp-err')
        }
    )

image.png

上图的输出结果中,我们看到输出的cp的状态为pending,then函数的回调函数没有执行。

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

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

class CustomPromise {
    constructor(initFunc) {
        this.status = 'pending'
        this.result = undefined
        this.onFulfilledFunc = undefined
        this.onRejectedFunc = undefined
        try {
            initFunc(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
        }
    }

    resolve(value) {
        if (this.status !== 'pending') return
        this.status = 'fulfilled'
        this.result = value
        this.onFulfilledFunc && this.onFulfilledFunc(this.result)
    }

    reject(value) {
        if (this.status !== 'pending') return
        this.status = 'rejected'
        this.result = value
        this.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 = onFulfilled
            this.onRejectedFunc = onRejected
        }
    }
}

在这里插入图片描述

这里我们看到上面程序中CustomPromis的then函数输出也在最后输出了出来。所以如果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')
})

let p = new Promise((resolve, reject) => { resolve(222) })

let pp = p
    .then(
        res => {
            console.log(res, 'pp-res')
        },
        err => {
            console.log(err, 'pp-err')
        }
    )

image.png

从上图中看到,同步创建cp对象时,执行时没啥问题。如果把setTimeout函数注释取消呢?

image.png

我们发现,Promise那段逻辑输出的pp-res在前,1这个在后了,顺序正确。但是cp-res的输出内容消失了!为什么呢?在把setTimeout放开注释后,我们来看下执行流程:

  1. 创建CustomPromise对象cp,状态为pending、值为undefined,
  2. 执行第一个then函数,判断状态为pending,保存回调函数至onFulfilledFunc
  3. 执行第二个then函数,判断状态为pending,替换回调函数onFulfilledFunc!
  4. setTimeout函数到主线程执行,执行完成后执行替换后的onFulfilledFunc函数。

所以,为了支持多次调用,我们需要把保存的变量修改为数组。

class CustomPromise {
    constructor(initFunc) {
        this.status = 'pending'
        this.result = undefined
        this.onFulfilledFuncList = []
        this.onRejectedFuncList = []
        try {
            initFunc(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
        }
    }

    resolve(value) {
        if (this.status !== 'pending') return
        this.status = 'fulfilled'
        this.result = value
        if (this.onFulfilledFuncList.length > 0) {
            this.onFulfilledFuncList.forEach(func => func(this.result))
        }
    }

    reject(value) {
        if (this.status !== 'pending') return
        this.status = 'rejected'
        this.result = value
        if (this.onRejectedFuncList.length > 0) {
            this.onRejectedFuncList.forEach(func => func(this.result))
        }
    }

    then(onFulfilled, onRejected) {
        // 判断入参是否为函数
        let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        let 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)
        }
    }
}

image.png

如此,这般。

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 => value
     let 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')

let p = new Promise((resolve, reject) => { resolve(222) })

let pp = p
    .then(
        res => {
            console.log(res, 'pp-res')
        },
        err => {
            console.log(err, 'pp-err')
        }
    )

image.png

  1. 这里then的回调函数没有返回,其实是按undefined处理(原始数据类型)

  2. 如果then返回CustomPromise类型,执行CustomPromise中then方法第12行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 => value
     let 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 => value
    let 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')

这段逻辑输出为: image.png 先执行同步任务(第五行),再执行微任务(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')

执行结果为:

image.png

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

class CustomPromise {
    constructor(initFunc) {
        this.status = 'pending'
        this.result = undefined
        this.onFulfilledFuncList = []
        this.onRejectedFuncList = []
        try {
            initFunc(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
        }
    }

    resolve(value) {
        if (this.status !== 'pending') return
        this.status = 'fulfilled'
        this.result = value
        if (this.onFulfilledFuncList.length > 0) {
            queueMicrotask(() => {
                this.onFulfilledFuncList.forEach(func => func(this.result))
            })
        }
    }

    reject(value) {
        if (this.status !== 'pending') return
        this.status = 'rejected'
        this.result = value
        if (this.onRejectedFuncList.length > 0) {
            queueMicrotask(() => {
                this.onRejectedFuncList.forEach(func => func(this.result))
            })
        }
    }

    then(onFulfilled, onRejected) {
        // 判断入参是否为函数
        let realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        let 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函数的回调函数执行时添加到微任务队列。此时的输出结果为: image.png

搭建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')
})

image.png

  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) {
    // 返回CustomPromise
    return 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等方法就不多介绍了,如果感兴趣可以点这里看一下,非常详细。

写文不易,点赞鼓励一下吧!

参考:juejin.cn/post/748079… zhuanlan.zhihu.com/p/183801144 zhuanlan.zhihu.com/p/451254515