从0实现一个基本的Promise类(五)

60 阅读6分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

前言

“手写Promise”系列作为前端常考点,深度检查了应聘者对Promise使用以及规范的理解,忏愧的是本人也是最近在完全通关这一模块。

此篇作为复习笔记,以及学习总结。

手写Promise包含以下知识点 👇:

  • Promise基础知识
  • Class 类
  • 改变this指向 (call、apply和bind)
  • 事件循环 Event Loop

其中Promise基础知识如果不了解或有遗忘的同学可以参考我这篇文章:《简单明了的Promise基本知识点》

本篇作为该系列最后一章,将讲解如何实现符合PromiseA+ 规范的完整Promise类,细节为👇

  • Promise/A+规范2.3
  • 实现**resolvePromise**

Promise/A+规范2.3

Promises/A+

vPl7bF.png

如上Promise规范中,运行 [[Resolve]](promise, x) 需遵循以下步骤:

2.3.1 传入参数中x 与 promise 相等

如果 promisex相同/为同一对象,那么用reject抛出一个 TypeError的错误。

2.3.2 传入参数中x 为 Promise

如果 x 的类型为 Promise ,则使 promise 接受 x 的状态

  • 2.3.2.1 如果 x 处于等待状态, promise 需保持为等待态直至 x 被执行或拒绝
  • 2.3.2.2 如果 x 处于执行状态,用相同的值执行 promise
  • 2.3.2.3 如果 x 处于拒绝状态,用相同的据因拒绝 promise

2.3.3传入参数中 x 为对象或函数

如果 x 为对象或者函数:

  • 2.3.3.1 把 x.then 赋值给 then

  • 2.3.3.2 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise

  • 2.3.3.3 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:

    • 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)

    • 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise

    • 2.3.3.3.3 如果 resolvePromiserejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用

    • 2.3.3.3.4 如果调用 then 方法抛出了异常 e

      • 2.3.3.3.4.1 如果 resolvePromiserejectPromise 已经被调用,则忽略之
      • 2.3.3.3.4.2 否则以 e 为据因拒绝 promise
    • 2.3.3.4 如果 then 不是函数,以 x 为参数执行 promise

▪ 2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise

实现**resolvePromise**

一次性消化这么长的规范想必还是有点困难,不过我们按照规则一条条实现就会简单许多了😏

首先创建[[Resolve]](promise, x)这个函数。

我们将[[Resolve]](promise, x)命名为resolvePromise(promise2,x)

function resolvePromise(promise2, x){}

接着依次实现规范2.3细则,首先👇

2.3.1 如果 promisex相同/为同一对象,那么用reject抛出一个 TypeError的错误。

在这里面我们发现会运用到传入的promise中的reject方法以及后文还会用到resolve方法,所以我们修改下resolvePromise👇

function resolvePromise(promise2,x,resolve,reject){
    
}

参数解释

  • promise2:then方法新返回的promise对象
  • xthen中调用onFulfilledonReject函数的返回值
  • resolve:promise2的成功执行函数
  • reject:promise2的拒绝执行函数

接着我们在原有的then方法中使用它👇

 then(onFulfilled,onRejected){
     ....
        const promise2 = new myPromise((resolve,reject)=>{
            if (this.PromiseState == myPromise.FULFILLED){
                setTimeout(()=>{
                    try {
                        const value = onFulfilled(this.PromiseResult)
                        if (value != undefined){
                            resolvePromise(promise2,value,resolve,reject)
                        }
                    }catch (e){
                        reject(e)
                    }
                })
            }else if(this.PromiseState == myPromise.REJECTED){
                setTimeout(()=>{
                    try {
                        const value = onRejected(this.PromiseResult)
                        if (value != undefined){
                            resolvePromise(promise2,value,resolve,reject)
                        }
                    }catch (e){
                        reject(e)
                    }
                })
            }
            ....
        })
        return promise2
    }

准备工作做完,现在我们只用专心的实现resolvePromise了👇

function resolvePromise(promise2,x,resolve,reject){
    if (promise2  == x){
        return reject(new TypeError())
    }
}

下一条~2.3.2 传入参数中x 为 Promise

function resolvePromise(promise2,x,resolve,reject){
    if (promise2  == x){
        return reject(new TypeError())
    }
    //2.3.2判断传入参数是否为Promise
    if (x instanceof myPromise){
        //2.3.2.1 如果 `x` 处于等待状态, `promise` 需保持为等待态直至 `x` 被执行或拒绝
        if (x.PromiseState == myPromise.PENDING){
           x.then(y => {
               resolvePromise(promise2, y, resolve, reject)
           }, reject);
        }else if(x.PromiseState == myPromise.FULFILLED){
            // 2.3.2.2 如果 x 处于执行态,用相同的值执行 promise
              resolve(x.PromiseResult);
        }else if (x.PromiseState === myPromise.REJECTED) {
            // 2.3.2.3 如果 x 处于拒绝态,用相同的据因拒绝 promise
              reject(x.PromiseResult);
        }
​
    }
}

2.3.3传入参数中 x 为对象或函数

2.3.4 如果 x 不为对象或者函数,以 x 为参数执行 promise

在判断x为对象或者函数时,需要注意**null**在使用typeof时也会被识别为一个对象。所以我们应当首先就排除null类型。

function resolvePromise(promise2,x,resolve,reject){
    if (promise2  == x){
        return reject(new TypeError())
    }
    //2.3.2判断传入参数是否为Promise
    if (x instanceof myPromise){
        //2.3.2.1 如果 `x` 处于等待状态, `promise` 需保持为等待态直至 `x` 被执行或拒绝
        if (x.PromiseState == myPromise.PENDING){
            resolvePromise(promise2,x,resolve,reject)
        }else if(x.PromiseState == myPromise.FULFILLED){
            // 2.3.2.2 如果 x 处于执行态,用相同的值执行 promise
              resolve(x.PromiseResult);
        }else if (x.PromiseState === myPromise.REJECTED) {
            // 2.3.2.3 如果 x 处于拒绝态,用相同的据因拒绝 promise
              reject(x.PromiseResult);
        }
    }else if(x != null && (typeof x === 'object' || typeof x == 'function')){
        let then;
        try {
            then = x.then
        }catch (e) {
            reject(e)
        }
        if (typeof then == 'function'){
            //2.3.3.3.3 如果 `resolvePromise` 和 `rejectPromise` 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
            let called = false
            try{
                then.call(x,
                    // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
                    y =>{
                        if (called)return //如果已调用,则采取首次调用
                        called = true
                        resolvePromise(promise2, y, resolve, reject);
                    },
                    // 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                    r =>{
                        if (called)return //如果已调用,则采取首次调用
                        called = true
                        reject(r);
                    })
            }catch (e) {
                //2.3.3.3.4 如果调用 `then` 方法抛出了异常 `e`:
                //2.3.3.3.4.1 如果 `resolvePromise` 或 `rejectPromise` 已经被调用,则忽略之
                if (called)return
                //2.3.3.3.4.2 否则以 `e` 为据因拒绝 `promise`
                called = true
                reject(e)
            }
        }else{
            //2.3.3.4 如果 `then` 不是函数,以 `x` 为参数执行 `promise`
            resolve(x)
        }
    }else{
        //2.3.4 如果 `x` 不为对象或者函数,以 `x` 为参数执行 `promise`
        resolve(x)
    }
}

到此我们就完成了2.3规范中所有的细则,接着开始2.? ........好像我们已经完成所有的规范了!

大功告成🤩

完整代码

class myPromise{
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';
    constructor(func){
        this.PromiseState =  myPromise.PENDING
        this.PromiseResult = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        try{
            func(this.resolve.bind(this),this.reject.bind(this))
        }catch (e){
            this.reject(e)
        }
    }
    resolve(result){
        if(this.PromiseState == myPromise.PENDING){
            this.PromiseState = myPromise.FULFILLED
            this.PromiseResult = result
            setTimeout(()=>{
                this.onFulfilledCallbacks.forEach(callback=>{callback(result)})
            })

        }
    }
    reject(reason){
        if (this.PromiseState == myPromise.PENDING){
            this.PromiseState = myPromise.REJECTED
            this.PromiseResult = reason
            setTimeout(()=>{
                this.onRejectedCallbacks.forEach(callback=>{callback(reason)})
            })

        }
    }
    then(onFulfilled,onRejected){
        onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected == 'function' ? onRejected : reason =>{ throw reason }
        const promise2 = new myPromise((resolve,reject)=>{
            if (this.PromiseState == myPromise.FULFILLED){
                setTimeout(()=>{
                    try {
                        const value = onFulfilled(this.PromiseResult)
                        if (value != undefined){
                            resolvePromise(promise2,value,resolve,reject)
                        }
                    }catch (e){
                        reject(e)
                    }
                })
            }else if(this.PromiseState == myPromise.REJECTED){
                setTimeout(()=>{
                    try {
                        const value = onRejected(this.PromiseResult)
                        if (value != undefined){
                            resolvePromise(promise2,value,resolve,reject)
                        }
                    }catch (e){
                        reject(e)
                    }
                })
            }else if(this.PromiseState === myPromise.PENDING){
                this.onFulfilledCallbacks.push(onFulfilled)
                this.onRejectedCallbacks.push(onRejected)
            }
        })
        return promise2
    }
}
function resolvePromise(promise2,x,resolve,reject){
    if (promise2  == x){
        return reject(new TypeError())
    }
    //2.3.2判断传入参数是否为Promise
    if (x instanceof myPromise){
        //2.3.2.1 如果 `x` 处于等待状态, `promise` 需保持为等待态直至 `x` 被执行或拒绝
        if (x.PromiseState == myPromise.PENDING){
            resolvePromise(promise2,x,resolve,reject)
        }else if(x.PromiseState == myPromise.FULFILLED){
            // 2.3.2.2 如果 x 处于执行态,用相同的值执行 promise
              resolve(x.PromiseResult);
        }else if (x.PromiseState === myPromise.REJECTED) {
            // 2.3.2.3 如果 x 处于拒绝态,用相同的据因拒绝 promise
              reject(x.PromiseResult);
        }
    }else if(x != null && (typeof x === 'object' || typeof x == 'function')){
        let then;
        try {
            then = x.then
        }catch (e) {
            reject(e)
        }
        if (typeof then == 'function'){
            //2.3.3.3.3 如果 `resolvePromise` 和 `rejectPromise` 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
            let called = false
            try{
                then.call(x,
                    // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
                    y =>{
                        if (called)return //如果已调用,则采取首次调用
                        called = true
                        resolvePromise(promise2, y, resolve, reject);
                    },
                    // 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                    r =>{
                        if (called)return //如果已调用,则采取首次调用
                        called = true
                        reject(r);
                    })
            }catch (e) {
                //2.3.3.3.4 如果调用 `then` 方法抛出了异常 `e`:
                //2.3.3.3.4.1 如果 `resolvePromise` 或 `rejectPromise` 已经被调用,则忽略之
                if (called)return
                //2.3.3.3.4.2 否则以 `e` 为据因拒绝 `promise`
                called = true
                reject(e)
            }
        }else{
            //2.3.3.4 如果 `then` 不是函数,以 `x` 为参数执行 `promise`
            resolve(x)
        }
    }
}

End

本人也是反复琢磨了俩周Promise规范实现,才总结出一系列的文章。看到这里的朋友可能仍还是不太清楚,不着急自己从0按照我的思路多实现就能够完美掌握了。

\