从0到1 手写Promise,我不信你学不会

57 阅读19分钟

手动实现一个Promise-----XPromise

代码有注释,前面部分可以按顺序一部分一部分的看,循序渐进,可以试着敲一敲,感觉也很好理解,我也是学习来的,欢迎大家共同探讨(虽然这个没啥可探讨的~~)

const _promise=new XPromise((resolve,reject)=>{
    resolve('成功')
    reject('失败')
})

_promise.then(res=>{
    console.log('then',res);
}).catch(err=>{
    console.log('catch',err);
}).finally(()=>{
    console.log('finally');
})

主要有二个大模块

  • 核心功能
  • 方法
  • 外加测试

核心功能

通过new去实例化一个promise对象,里面传入一个回调函数可以执行异步操作,成功通过resolve传入成功的值,失败通过reject传入失败的值...算了,话不多说,直接开撸吧,用过promise的你应该都清楚一些基本介绍。

进入正题

将从以下五个模块逐步推进,按序号步骤看

graph LR
构造函数 --> 状态及原因 --> then实例方法-->异步任务-->链式编程
    1. 构造函数
    • 1.1 定义一个XPromise类(自定义)
    • 1.2 类里面整一个构造函数,接收传来的参数,参数包含了resolve,reject方法
    • 1.3 构造函数内部定义resolve和reject方法
    • 1.4 执行回调函数
    //1.1定义XPromise类
    class XPromise{
        // 1.2添加构造函数
        constructor(callback){
             // 1.3定义resolve/reject方法
            const resolve=(res)=>{
                console.log(res);//成功
            }
            const reject=(error)=>{
                console.log(error);//失败
            }
            // 1.4执行回调函数
            callback(resolve,reject)
        }
    }
    
    const _promise=new XPromise((resolve,reject)=>{
        //resolve('成功')
        //reject('失败')
    })
    

这里你就完成了基础搭建

状态和原因

    1. 按步骤操作
    • 2.1 定义全局状态PENDING
    • 2.2 添加status(记录状态)、reason(记录原因)
    • 2.3 添加状态判断(一旦改变就不可再改变, 如果不加判断,下面同时执行,reject会覆盖resolve,可尝试下)
    • 2.4 设置状态和原因
    // 2.1 定义全局状态 
    const PENDING="pending",FULFILLED="fulfilled",REJECTED="rejected";
    class XPromise{
        // 2.2 用status实例属性设置状态
        status=PENDING //默认状态是pending
        // 2.2 用reason实例属性来设置原因
        reason=undefined //初始原因是undefined
        constructor(callback) {
            const resolve = (res) => {
            //2.3 这里添加判断 让状态不可逆
                if (this.status === PENDING) {
                    // 2.4 成功是fulfilled 状态
                    this.status = FULFILLED
                    // 2.4 记录成功原因
                    this.reason = res
                }
            }
            const reject = (error) => {
            //2.3 这里添加判断 让状态不可逆
                if (this.status === PENDING) {
                    // 2.4 失败是rejected 状态
                    this.status = REJECTED
                    // 2.4 记录失败原因
                    this.reason = error
                }
            }
            callback(resolve, reject)
        }
    
    const _promise=new XPromise((resolve,reject)=>{
        console.log('第一步执行了');
        resolve('成功')
        // reject('失败')
    })
    
    

then方法

  • 先看看then的用法
    • image.png
    1. 写一个then
    • 3.1 添加实例化then方法

    • 3.2 判断传入的是否是函数类型,如果是就执行成功和失败的回调,如果不是就根据MDN文档developer.mozilla.org/zh-CN/docs/…

      • image.png
    • 3.3 根据不同的状态执行回调,并传入原因

      const PENDING="pending",FULFILLED="fulfilled",REJECTED="rejected";
      class XPromise{
          status=PENDING //默认状态是pending
          reason=undefined //初识原因是undefined
          constructor(callback) {
              const resolve = (res) => {
                  if (this.status === PENDING) {
                      this.status = FULFILLED
                      this.reason = res
                  }
              }
              const reject = (error) => {
                  if (this.status === PENDING) {
                      this.status = REJECTED
                      this.reason = error
                  }
              }
      
              // 执行回调函数
              callback(resolve, reject)
          }
          //3.1 添加实例化then方法
          //onFulfilled,onRejected 对应的其实就是then后面的res和error,参考上面then方法的图
          then(onFulfilled,onRejected){
              //3.2 这里首先得判断传入的是不是一个函数 如果是函数就执行 不是函数 就参考的MDN文档直接返回
              onFulfilled=typeof onFulfilled==='function'?onFulfilled:(x) => x
              //如果把下面then里面的error去掉,就不是函数,就会报错,调用reject就会输出'失败',因为这里throw抛出异常了
              onRejected=typeof onRejected==='function'?onRejected:(x) => { throw x; }
              //3.3 .根据不同的状态执行不同的方法,返回不同的值
               if(this.status===FULFILLED){
                  // 成功状态 执行成功函数 并且传入执行原因
                  onFulfilled(this.reason)
              }else if(this.status===REJECTED){
                  // 失败状态 执行失败函数 并且传入执行原因
                  onRejected(this.reason)
              }
          }
      }
      
      
      const _promise = new XPromise((resolve, reject) => {
          console.log('第一步执行了');
          // resolve('成功')
          // reject('失败')
      })
      _promise.then(res => {
          console.log('resolve进入', res);
      }, error => {
          console.log('reject进入', error);
      })
      
    1. 让then支持异步和多次调用
    • 如果在setTimeout中执行,下面的then是不会输出的

    • 4.1 定义一个私有属性(#表示私有) 用来存储回调函数

    • 4.2 在PENDING状态将回调函数存储起来

    • 4.3 在执行成功和失败的方法里面将存储的回调函数取出来执行

      const _promise = new XPromise((resolve, reject) => {
          // console.log('第一步执行了');
          setTimeout(() => {
              resolve('成功')
          }, 2000);
          // reject('失败')
      })
      _promise.then(res => {
          console.log('resolve1进入', res);
      }, error => {
          console.log('reject1进入', error);
      })
      
      _promise.then(res => {
          console.log('resolve2进入', res);
      }, error => {
          console.log('reject2进入', error);
      })
      
    • 实现支持异步调用并支持多次then调用

       const PENDING="pending",FULFILLED="fulfilled",REJECTED="rejected";
       class XPromise{
           status=PENDING //默认状态是pending
           reason=undefined //初识原因是undefined
           // 4.1 定义一个私有属性 用来存储 成功和失败的方法
           #method=[] //[{onFulfilled,onRejected}...]
           constructor(callback) {
               const resolve = (res) => {
                   if (this.status === PENDING) {
                       this.status = FULFILLED
                       this.reason = res
                       //4.3 存储的取出来挨个执行
                       this.#method.forEach(({onFulfilled})=>onFulfilled(this.reason))
                   }
               }
               const reject = (error) => {
                   if (this.status === PENDING) {
                       this.status = REJECTED
                       this.reason = error
                      //4.3 存储的取出来挨个执行  
                      this.#method.forEach(({onRejected})=>onRejected(this.reason))
                   }
               }
               callback(resolve, reject)
           }
           then(onFulfilled,onRejected){
               onFulfilled=typeof onFulfilled==='function'?onFulfilled:(x) => x
               onRejected=typeof onRejected==='function'?onRejected:(x) => { throw x; }
                if(this.status===FULFILLED){
                   onFulfilled(this.reason)
               }else if(this.status===REJECTED){
                   onRejected(this.reason)
               }else if(this.status===PENDING){
                   // 4.2 回调函数就存起来了
                   this.#method.push({onFulfilled,onRejected})
               }
           }
       }
      
      
       const _promise = new XPromise((resolve, reject) => {
           console.log('第一步执行了');
           setTimeout(() => {
               resolve('成功')
           }, 2000);
       })
       _promise.then(res => {
           console.log('resolve1进入', res);
       }, error => {
           console.log('reject1进入', error);
       })
        _promise.then(res => {
           console.log('resolve2进入', res);
       }, error => {
           console.log('reject2进入', error);
       })
      

      注意这里不是链式调用,这里只是同一个对象,多次调用then方法

异步任务

  • promise 本身是同步的,但它实际上是基于异步编程的解决方案
console.log('我是第一个');
const _promise = new XPromise((resolve, reject) => {
    resolve('我是第二个')
})
_promise.then(res => {
    console.log('resolve1进入', res);
}, error => {
    console.log('reject1进入', error);
})

console.log('我是最后一个');
//不出意外 这下面的执行顺序就是从上到下
// 我是第一个
// resolve1进入 我是第二个
// 我是最后一个
  • 很明显,这样的输出结果不是我们想要的,我们希望then里面的在最后,那么我们这里就采用这三个核心API来实现(因为有浏览器兼容性问题,所以采用这三个API):
    • queueMicrotaskMutationObserversetTimeout(兜底)
    • 为了能达到异步任务,我们写一个公共的方法来执行
    // 为了能达到异步任务 我们写一个函数 参数是一个回调函数
    function runAsyncTask(callback){
        // 为了兼容 我们依次采用queueMicrotask、MutationObserver判断
        if(typeof queueMicrotask==='function'){
            queueMicrotask(callback)
        }else if(typeof MutationObserver==='function'){
            // 创建MutationObserver实例化对象
            const m=new MutationObserver(callback)
            //创建一个DOM节点
            const vnode=document.createElement('p')
            //通过MutationObserver 观察这个DOM节点
            m.observe(vnode,{childList:true})
            //子节点发生改变就会触发回调函数
            vnode.innerText="xxx"// 这里内容改变就会自动执行回调函数
        }else{
            // 兼容性都不满足 就用setTimeout 兜底
            setTimeout(callback,0)
        }
    }
    
    • 5.1 然后我们在then方法调用这个函数
     then(onFulfilled,onRejected){
        onFulfilled=typeof onFulfilled==='function'?onFulfilled:(x) => x
        onRejected=typeof onRejected==='function'?onRejected:(x) => { throw x; }
        if(this.status===FULFILLED){
            //5.1 这里通过runAsyncTask改成异步任务即可
            runAsyncTask(()=>{
                onFulfilled(this.reason)
            })
        }else if(this.status===REJECTED){
            //5.1 这里通过runAsyncTask改成异步任务即可
            runAsyncTask(()=>{
                onRejected(this.reason)
            })
        }else if(this.status===PENDING){
            //5.1 PENDING 状态 我们就用runAsyncTask对onFulfilled和onRejected包装然后push到#method中
             this.#method.push({
                onFulfilled: () => {
                    runAsyncTask(onFulfilled(this.reason))
                }, onRejected: () => {
                    runAsyncTask(onRejected(this.reason))
                }
            })
        }
    }
    
    执行到这里,你动手体验下就知道感受了!

链式编程

  • 这里应该可以理解为套娃
const _promise = new XPromise((resolve, reject) => {
    resolve('我是实例化')
})

_promise.then(res=>{
    console.log('这里输出实例化的结果',res)
    return '给then2'
}).then(res=>{
    console.log('这里应该输出第一个then的结果',res);
})

image.png

  • 那么具体怎么实现呢?
    • 本质上其实就是第一个then方法需要返回一个支持用.then调用的东西
    • 6.1 那么我们只需要再实例化一个XPromise不就可以用then方法了
    • 6.2 然后我们处理下返回值
      • 6.2.1 先获取返回值
      • 6.2.2 处理返回值
      • 6.2.3 处理异常 通过try/catch
    • 6.3 还需要考虑如果它是返回一个promise实例,那么我就需要对结果进行判断
      • 6.3.1 如果是promise实例,就调用then方法返回
    • 6.4 这里需要注意的一个点,就是如果在then回调函数里面,直接返回then方法的返回值 那么就会出现错误,原生Promise是会抛出错误的
      • 6.4.1 所以我们也加一个 一句代码而已
    _promise.then(res => {
        console.log('这里输出实例化的结果', res)
        // 情况1 return '给then2'
        // 情况2 throw 'throw抛出异常了'
        // 情况3 return new XPromise((resolve,reject)=>{
        //        resolve('新实例')
        //      })
    }).then(res => {
        console.log('这里应该输出第一个then的结果', res);
    }, err => {
    //如果抛出异常 捕获的异常结果 会在这里输出
        console.log('这里异常了', err);
    })
    //6.4 注意 这里在then回调函数里面,直接返回then方法的返回值 那么就会出现错误
    const _promise1=_promise.then(res => {
          //_promise1 等于const定义的 _promise1
            return _promise1 
        }, error => {
            console.log('reject1进入', error);
        })
            //这里按道理是应该会抛出错误的 Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
        _promise1.then(res=>{
            console.log('_promise1的成功结果',res);
        },err=>{
            console.log('_promise1的失败结果',err);
        })
    
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x
        onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x; }
    
        // 6.1 这里实例化XPromise 就可以继续使用.then方法了
        const newXPromise=new XPromise((resolve,reject)=>{
            if (this.status === FULFILLED) {
                runAsyncTask(() => {
                    try {
                        // 6.2.1 将返回值先获取到
                        const returnedValue= onFulfilled(this.reason)
                        //6.2.2 将返回值传给下一个then
                        // resolve(returnedValue)
                        // 6.4.1 就在这里判断一下 如果是相等抛出一个错误,而且输出是_promise1的失败结果,Chaining cycle detected for promise #<Promise>
                         if (returnedValue === newXPromise) {
                            throw new TypeError('Chaining cycle detected for promise #<Promise>')
                        }
                        // 针对情况3 我们需要做一下处理
                        // 6.3 判断是否是XPromise对象
                        if(returnedValue instanceof XPromise){
                             // 6.3.1 如果是XPromise对象就进一步调用then,然后返回
                            returnedValue.then(res=>resolve(res),err=>reject(err))
                        }else{
                            //如果不是XPromise对象将返回值直接传给下一个then
                            resolve(returnedValue)
                        }
                        
                    } catch (error) {
                        //6.2.3 这里直接捕获异常 传给newXPromise 
                        reject(error)
                    }
                })
            } else if (this.status === REJECTED) {
                // 失败状态 执行失败函数 并且传入执行原因
                runAsyncTask(() => {
                    onRejected(this.reason)
                })
            } else if (this.status === PENDING) {
                // 4.2 回调函数就存起来了
                this.#method.push({
                    onFulfilled: () => {
                        runAsyncTask(()=>onFulfilled(this.reason))
                    }, onRejected: () => {
                        runAsyncTask(()=>onRejected(this.reason))
                    }
                })
            }
         })
        return newXPromise
    }
    
    
    • 6.5 这里我们处理了FULFILLED 状态的返回值,那么其实REJECTED也是一样的,所以我们将一样的提取出来,写一个公共的方法
function resolvePromie(returnedValue, newXPromise, resolve, reject) {
    if (returnedValue === newXPromise) {
        throw new TypeError('传入的值错误,Chaining cycle detected for promise #<Promise>')
    }
    if (returnedValue instanceof XPromise) {
        returnedValue.then(res => resolve(res), err => reject(err))
    } else {
        //将返回值传给下一个then
        resolve(returnedValue)
    }
}

//then 模块进一步改写一下
 then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x
        onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x; }

        // 6.1 这里实例化XPromise 就可以继续使用.then方法了
        const newXPromise=new XPromise((resolve,reject)=>{
            if (this.status === FULFILLED) {
                runAsyncTask(() => {
                    try {
                        // 将返回值先获取到
                        const returnedValue = onFulfilled(this.reason)
                        //6.5 调用封装的方法处理
                        resolvePromie(returnedValue, newXPromise, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            } else if (this.status === REJECTED) {
                // 失败状态 执行失败函数 并且传入执行原因
                runAsyncTask(() => {
                    try {
                        const returnedValue = onRejected(this.reason)
                        //6.5 调用封装的方法处理
                        resolvePromie(returnedValue, newXPromise, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            } else if (this.status === PENDING) {
                 this.#method.push({
                    onFulfilled: () => {
                        runAsyncTask(() =>{
                            try {
                            //6.5 调用封装的方法处理
                                const returnedValue = onFulfilled(this.reason)
                                resolvePromie(returnedValue, newXPromise, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }, onRejected: () => {
                        runAsyncTask(() => {
                            try {
                            //6.5 调用封装的方法处理
                                const returnedValue = onRejected(this.reason)
                                resolvePromie(returnedValue, newXPromise, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                })
            }
         })
        return newXPromise
    }
    //用这里的代码测试下 查看输出结果
    const _promise = new XPromise((resolve, reject) => {
        resolve('我是实例化')
    })
    const _promise1 = _promise.then(res => {
        // return 1
        // throw 'error'
        // return _promise1
        return new XPromise((resolve,reject)=>{
            resolve('再一次')
        })
    }, error => {
        console.log('reject1进入', error);
    })
    //成功失败 都会在这里输出的
    _promise1.then(res => {
        console.log('_promise1的成功结果', res);
    }, err => {
        console.log('_promise1的失败结果', err);

    })

到这里,核心功能就已经完成了,每一步我都标记的有序号,根据序号去看注释部分,自己尝试下吧

方法

实例方法

之前我们已经实现了then方法,那么还有catchfinally

catch
  • 7.1 其实就是添加和then一样的一个实例方法,参考下MDN就明白了
  • 7.2 但是如果实例化的时候出现异常的话,我们就需要处理下异常
    • 实例化的时候 捕获异常在 1.4 执行回调函数 的时候处理

image.png

//7.1 在then下面添加catch实例方法,内部调用then方法即可
    catch(onRejected){
        return this.then(undefined,onRejected)
    }
//7.2 然后改一下构造函数里面的返回,加一个try/catch包裹,返回错误即可
 // 1.4 执行回调函数
    try {
        callback(resolve, reject)
    } catch (error) {
        reject(error)
    }
finally

看MDN的描述就很简单,就是内部调用then方法,传入两个一样的参数即可

image.png

  • 8.1 在catch下面添加finally实例方法
 //8.1 添加finally实例方法
    finally(onFinally){
        return this.then(onFinally,onFinally)
    }

上面我们实现了实例方法,下面就实现静态方法

静态方法

graph LR
resolve --> reject-->race-->all-->allSettled-->any
XPromise.resolve(值)
  • 对传入的值进行判断(可参考MDN)
    • 9.1 如果是普通值,转为XPromise并返回
      • 9.2 如果是XPromise,直接返回
      • 9.3 静态方法测试代码
    • 9.4 reject方法
      • 9.4.1 Promise.reject()  静态方法返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数(MDN参考)。
      • 9.4.2 reject静态方法测试代码
    // resolve静态方法
    static resolve(value){
        // 9.2 判断是否是XPromise
        if(value instanceof XPromise){
            return value
        }
        // 9.1 如果是普通值
        return new XPromise((resolve)=>resolve(value))
    }
    // 9.4.1 reject静态方法
    static reject(value){
        return new XPromise((resolve,reject)=>reject(value))
    }
    
    // 9.3 静态方法测试 
    XPromise.resolve(new XPromise((resolve,reject)=>{
        // resolve('静态方法初始化')
        // reject('静态方法初始化')
        // throw '静态方法初始化'
    })).then(res=>{
        console.log('静态方法成功',res);
    },error=>{
        console.log('静态方法失败',error);
    })
    XPromise.resolve('普通值').then(res=>{
        console.log(res);
    })
    // 9.4.2 静态方法测试代码
    XPromise.reject('reject普通值').catch(res=>{
        console.log(res);
    })
  • race方法
  • 其实就是谁最快返回结果
  • 测试代码

image.png

    //静态 race方法
    static race(value){
            // 返回XPromise
            return new XPromise((resolve,reject)=>{
                // 本来接收的是一个数组,但是不排除非数组;判断是否是数组不是数组抛出错误
                if(!Array.isArray(value)){
                   return reject(new TypeError('不是一个数组哦~'))
                }
                // 这里传递的肯定是一个数组 所以我们遍历数组
                value.forEach(item=>{
                    // 但是我们不一定数组里面都是XPromise实例,所以直接将他们都转为XPromise实例
                    XPromise.resolve(item).then(res=>{
                        resolve(res)
                    },err=>{
                        reject(err)
                    })
                })
            })
        }
        
        
     //race测试代码 
    const _promise1=new XPromise((resolve,reject)=>{
        setTimeout(() => {
            resolve('race1实例')
        }, 1000);
    })
    const _promise2=new XPromise((resolve,reject)=>{
        setTimeout(() => {
            reject('race2实例')
        }, 500);
    })
    //如果去掉普通值,那么结果肯定是输出:(race结果失败,race2实例)
    XPromise.race([_promise1,_promise2,'普通值']).then(res=>{
        console.log('race结果正常',res);
    },err=>{
        console.log('race结果失败',err);
    })
  • all方法
  • 要么都成功,要么返回第一个失败的
  • 首先要判断是否是数组
    • 空数组直接返回
    • 数组的话 进一步处理

image.png

static all(value) {
        // 返回XPromise实例
        return new XPromise((resolve, reject) => {
            // 判断是否是数组 不是数组 抛出错误
            if (!Array.isArray(value)) {
                return reject(new TypeError('不是一个数组哦~'))
            }
            //如果是空数组,直接返回
            value.length === 0 && resolve(value)

            //  为了让我们执行的顺序跟传入的顺序保持一致 
            //  定义一个数组,将结果按顺序存起来
            let results = []//存储结果 
            let count = 0 //记录返回的次数 
            value.forEach((item,index) => {
                // 但是我们不一定数组里面都是XPromise实例,所以直接将他们都转为XPromise实例
                XPromise.resolve(item).then(res => {
                    results[index] = res
                    count++
                    // 不能通过results长度判断来返回,如果最后一个最先返回,就没有获取到所有的结果
                    count === value.length && resolve(results)
                    // 保证结果的顺序跟数组的顺序一致
                }, err => {
                    // 只要第一个拒绝就返回
                    reject(err)
                })
            })
        })
    }
    
    //all 代码测试
    const _promise1 = new XPromise((resolve, reject) => {
        setTimeout(() => {
            resolve('all1实例')
        }, 1000);
    })
    const _promise2 = new XPromise((resolve, reject) => {
        setTimeout(() => {
            resolve('all2实例')
            reject('all2实例')//如果这里执行,那么结果一定是失败结果
        }, 500);
    })
    const _promise3 = 3
    XPromise.all([_promise1, _promise2,_promise3]).then(res => {
        console.log('all结果正常', res);
    }, err => {
        console.log('all结果失败', err);
    })
  • allSettled
    • 传的是数组,得到的也是一个对象数组
      • [{ status: 'fulfilled', value: 33 },{ status: 'rejected', reason: Error: 一个错误 }]
      • 结果顺序跟传入的数组顺序一致
    • 不是数组会报错
    • all方法基本一致,就是返回结果不一样
    • 不管成功还是失败,都在then的第一个回调函数
    //allSettled 静态方法
    static allSettled(value){
                 // 返回XPromise实例
            return new XPromise((resolve, reject) => {
                // 判断是否是数组 不是数组 抛出错误
                if (!Array.isArray(value)) {
                    return reject(new TypeError('不是一个数组哦~'))
                }
                //如果是空数组,直接返回
                value.length === 0 && resolve(value)

                let results = []//存储结果 
                let count = 0 //记录返回的次数 
                value.forEach((item,index) => {
                    // 但是我们不一定数组里面都是XPromise实例,所以直接将他们都转为XPromise实例
                    XPromise.resolve(item).then(res => {
                        results[index] = {status:FULFILLED,value:res}
                        count++
                        // 不能通过results长度判断来返回,如果最后一个最先返回,就没有获取到所有的结果
                        count === value.length && resolve(results)
                        // 保证结果的顺序跟数组的顺序一致
                    }, err => {
                        results[index] = {status:REJECTED,reason:err}
                        count++
                        // 不能通过results长度判断来返回,如果最后一个最先返回,就没有获取到所有的结果
                        count === value.length && resolve(results) //无论成功还是失败 都是已完成 用resolve
                    })
                })
            })
        }
        
        //allSettled 测试代码
        const _promise1 = new XPromise((resolve, reject) => {
            setTimeout(() => {
                resolve('allSettled1实例')
            }, 1000);
        })
        const _promise2 = new XPromise((resolve, reject) => {
            setTimeout(() => {
                reject('allSettled2实例')
            }, 500);
        })
        const _promise3 = 3
        XPromise.allSettled([_promise1, _promise2,_promise3]).then(res => {
            console.log('allSettled结果正常', res);//不管成功还是失败 都会进入这里
        }, err => {
            console.log('allSettled结果失败', err);//这里是不会进入的
        })
  • any
  • 都失败的话,会返回一个错误的数组,数组里面记录了每一个错误的原因
  • 空数组的话,也是一个错误,但是错误里面的数组是空的
    //静态 any方法
     static any(value){
            // 返回XPromise实例
            return new XPromise((resolve, reject) => {
                // 判断是否是数组 不是数组 抛出错误
                if (!Array.isArray(value)) {
                    return reject(new TypeError('不是一个数组哦~'))
                }
                //如果是空数组,直接返回
                // value.length === 0 && reject('不能是空数组,小可爱~~') new AggregateError Promise是这样抛的
                value.length === 0 && reject(new AggregateError(value,'不能是空数组呀'))

                let errors = []//存储错误结果; 正常的直接返回了 
                let count = 0 //记录返回的次数 
                value.forEach((item,index) => {
                    // 但是我们不一定数组里面都是XPromise实例,所以直接将他们都转为XPromise实例
                    XPromise.resolve(item).then(res => {
                        resolve(res)
                    }, err => {
                        errors[index] =err
                        count++
                        // 不能通过results长度判断来返回,如果最后一个最先返回,就没有获取到所有的结果
                        count === value.length && reject(new AggregateError(errors,'完蛋了~,都是reject')) 
                    })
                })
            })
        }
        
        // any 测试代码
        const _promise1 = new XPromise((resolve, reject) => {
            setTimeout(() => {
                reject('any1实例')
            }, 1000);
        })
        const _promise2 = new XPromise((resolve, reject) => {
            setTimeout(() => {
                reject('anyd2实例')
            }, 500);
        })
        const _promise3 = XPromise.reject(3)
        XPromise.any([_promise1, _promise2,_promise3]).then(res => {
            console.log('any结果正常', res);
        }, err => {
            console.log('any结果失败', err);
            //如果都是reject,会返回一个数组
            console.dir(err);//dir能看到详细报错数组信息
        })

到这里,基本都完成了,最后一个promise/A+规范测试了

单元测试

  • 使用社区提供的包promises-aplus-tests测试
  • 具体规范参考:promisesaplus.com/

完整代码

  • 删掉了部分注释,方便看的更清楚些
  • 这里的代码其实缺少符合A+规范,但是手写Promise大概流程就这样
  • 自己学习使用,代码没有做优化,很多地方可以优化(主要是懒)
const PENDING = "pending", FULFILLED = "fulfilled", REJECTED = "rejected";// 2.1 定义全局状态 

function runAsyncTask(callback) {
    if (typeof queueMicrotask === 'function') {
        queueMicrotask(callback)
    } else if (typeof MutationObserver === 'function') {
        const m = new MutationObserver(callback)
        const vnode = document.createElement('p')
        m.observe(vnode, { childList: true })
        vnode.innerText = "change"// 这里内容改变就会自动执行回调函数
    } else {
        setTimeout(callback, 0)
    }
}

function resolvePromie(returnedValue, newXPromise, resolve, reject) {
    if (returnedValue === newXPromise) {
        throw new TypeError('传入的值错误,Chaining cycle detected for promise #<Promise>')
    }
    if (returnedValue instanceof XPromise) {
        returnedValue.then(res => resolve(res), err => reject(err))
    } else {
        resolve(returnedValue)
    }
}

class XPromise {
    status = PENDING // 2.2 用status实例属性设置状态,默认状态是pending 
    reason = undefined //2.2 用reason实例属性来设置原因,初始原因是undefined
    #method = [] //[{onFulfilled,onRejected}...]

    constructor(callback) {
        const resolve = (res) => {
            if (this.status === PENDING) {
                this.status = FULFILLED // 2.4 成功是fulfilled 状态
                this.reason = res // 2.4 记录成功原因
                this.#method.forEach(({ onFulfilled }) => onFulfilled(this.reason))
            }
        }
        const reject = (error) => {
            if (this.status === PENDING) {
                this.status = REJECTED // 2.4 失败是rejected 状态
                this.reason = error // 2.4 记录失败原因
                this.#method.forEach(({ onRejected }) => onRejected(this.reason))
            }
        }
        try {
            callback(resolve, reject)
        } catch (error) {
            console.log('error', error);
            reject(error)
        }
    }

    //then实例方法
    then(onFulfilled, onRejected) {
        // 3.2 这里首先得判断传入的是不是一个函数 如果是函数就执行 不是函数 就参考的MDN文档直接返回
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x
        onRejected = typeof onRejected === 'function' ? onRejected : (x) => { throw x; }

        // 这里实例化XPromise 就可以继续使用.then方法了
        const newXPromise = new XPromise((resolve, reject) => {
            //3.3 根据不同的状态执行不同的方法,返回不同的值
            if (this.status === FULFILLED) {
                // 成功状态 执行成功函数 并且传入执行原因
                runAsyncTask(() => {
                    try {
                        // 将返回值先获取到
                        const returnedValue = onFulfilled(this.reason)
                        resolvePromie(returnedValue, newXPromise, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            } else if (this.status === REJECTED) {
                // 失败状态 执行失败函数 并且传入执行原因
                runAsyncTask(() => {
                    try {
                        const returnedValue = onRejected(this.reason)
                        resolvePromie(returnedValue, newXPromise, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            } else if (this.status === PENDING) {
                // 4.2 回调函数就存起来了
                this.#method.push({
                    onFulfilled: () => {
                        runAsyncTask(() => {
                            try {
                                const returnedValue = onFulfilled(this.reason)
                                resolvePromie(returnedValue, newXPromise, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }, onRejected: () => {
                        runAsyncTask(() => {
                            try {
                                const returnedValue = onRejected(this.reason)
                                resolvePromie(returnedValue, newXPromise, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        })
                    }
                })
            }
        })
        return newXPromise
    }

    //添加catch实例方法
    catch(onRejected) {
        this.then(undefined, onRejected)
    }

    //添加finally实例方法
    finally(onFinally) {
        return this.then(onFinally, onFinally)
    }

    /**-------静态方法--------- */
    static resolve(value) {
        if (value instanceof XPromise) {
            return value
        }
        return new XPromise((resolve) => resolve(value))
    }

    static reject(value) {
        return new XPromise((resolve, reject) => reject(value))
    }

    static race(value) {
        return new XPromise((resolve, reject) => {
            if (!Array.isArray(value)) {
                return reject(new TypeError('不是一个数组哦~'))
            }
            value.forEach(item => {
                XPromise.resolve(item).then(res => {
                    resolve(res)
                }, err => {
                    reject(err)
                })
            })
        })
    }

    static all(value) {
        return new XPromise((resolve, reject) => {
            if (!Array.isArray(value)) {
                return reject(new TypeError('不是一个数组哦~'))
            }
            value.length === 0 && resolve(value)
            let results = []//存储结果 
            let count = 0 //记录返回的次数 
            value.forEach((item,index) => {
                XPromise.resolve(item).then(res => {
                    results[index] = res
                    count++
                    count === value.length && resolve(results)
                }, err => {
                    reject(err)
                })
            })
        })
    }

    static allSettled(value){
        return new XPromise((resolve, reject) => {
            if (!Array.isArray(value)) {
                return reject(new TypeError('不是一个数组哦~'))
            }
            value.length === 0 && resolve(value)
            let results = []//存储结果 
            let count = 0 //记录返回的次数 
            value.forEach((item,index) => {
                XPromise.resolve(item).then(res => {
                    results[index] = {status:FULFILLED,value:res}
                    count++
                    count === value.length && resolve(results)
                }, err => {
                    results[index] = {status:REJECTED,reason:err}
                    count++
                    count === value.length && resolve(results) //无论成功还是失败 都是已完成 用resolve
                })
            })
        })
    }

    static any(value){
            return new XPromise((resolve, reject) => {
                if (!Array.isArray(value)) {
                    return reject(new TypeError('不是一个数组哦~'))
                }
                value.length === 0 && reject(new AggregateError(value,'不能是空数组呀'))
                let errors = []//存储错误结果 正常的 直接返回了 
                let count = 0 //记录返回的次数 
                value.forEach((item,index) => {
                    XPromise.resolve(item).then(res => {
                        resolve(res)
                    }, err => {
                        errors[index] =err
                        count++
                        count === value.length && reject(new AggregateError(errors,'不能是空数组呀')) //无论成功还是失败 都是已完成 用resolve
                    })
                })
            })
    }
}

    // 静态方法测试
    XPromise.resolve(new XPromise((resolve, reject) => {
        // resolve('静态方法初始化')
        // reject('静态方法初始化')
        // throw '静态方法初始化'
    })).then(res => {
        console.log('静态方法成功', res);
    }, error => {
        console.log('静态方法失败', error);
    })

    const _promise1 = new XPromise((resolve, reject) => {
        setTimeout(() => {
            reject('any1实例')
        }, 1000);
    })
    const _promise2 = new XPromise((resolve, reject) => {
        setTimeout(() => {
            reject('anyd2实例')
        }, 500);
    })
    const _promise3 = XPromise.reject(3)
    XPromise.any([_promise1, _promise2,_promise3]).then(res => {
        console.log('any结果正常', res);
    }, err => {
        console.log('any结果失败', err);
        console.dir(err);//dir能看到详细报错数组信息
    })

    // const _promise1 = new XPromise((resolve, reject) => {
    //     setTimeout(() => {
    //         resolve('allSettled1实例')
    //     }, 1000);
    // })
    // const _promise2 = new XPromise((resolve, reject) => {
    //     setTimeout(() => {
    //         reject('allSettled2实例')
    //     }, 500);
    // })
    // const _promise3 = 3
    // XPromise.allSettled([_promise1, _promise2,_promise3]).then(res => {
    //     console.log('allSettled结果正常', res);
    // }, err => {
    //     console.log('allSettled结果失败', err);
    // })

    // const _promise1 = new XPromise((resolve, reject) => {
    //     setTimeout(() => {
    //         resolve('all1实例')
    //     }, 1000);
    // })
    // const _promise2 = new XPromise((resolve, reject) => {
    //     setTimeout(() => {
    //         resolve('all2实例')
    //     }, 500);
    // })
    // const _promise3 = 3
    // XPromise.all([_promise1, _promise2,_promise3]).then(res => {
    //     console.log('all结果正常', res);
    // }, err => {
    //     console.log('all结果失败', err);
    // })
    // XPromise.race([_promise1,_promise2]).then(res=>{
    //     console.log('race结果正常',res);
    // },err=>{
    //     console.log('race结果失败',err);
    // })

    // XPromise.resolve('resolve普通值').then(res=>{
    //     console.log(res);
    // })
    // XPromise.reject('reject普通值').catch(res=>{
    //     console.log(res);
    // })

    // const _promise = new XPromise((resolve, reject) => {
    //     setTimeout(() => {
    //         // resolve('我是实例化')
    //         // reject('我是catch实例化')
    //         throw 'throw抛出异常了'
    //     }, 1000);
    // })
    // _promise.then(res => {
    //     console.log('实例方法成功', res);
    // }).catch(err => {
    //     console.log('实例方法失败', err);
    // }).finally(()=>{
    //     console.log('finally');
    // })


    // _promise.then(res => {
    //     console.log('resolve1进入', res);
    //     resolve('第一个then到第二个then执行')
    // }, error => {
    //     console.log('reject1进入', error);
    // })

    // _promise.then(res => {
    //     console.log('这里输出实例化的结果', res)
    //     // return '给then2'
    //     // throw 'throw抛出异常了'
    //     return new XPromise((resolve, reject) => {
    //         resolve('新实例')
    //     })
    // }).then(res => {
    //     console.log('这里应该输出第一个then的结果', res);
    // }, err => {
    //     console.log('这里异常了', err);
    // })
    // const _promise1 = _promise.then(res => {
    //     // return 1
    //     // throw 'error'
    //     // return _promise1
    //     return new XPromise((resolve, reject) => {
    //         resolve('再一次')
    //     })
    // }, error => {
    //     console.log('reject1进入', error);
    // })

    // _promise1.then(res => {
    //     console.log('_promise1的成功结果', res);
    // }, err => {
    //     console.log('_promise1的失败结果', err);

    // })

    // console.log('我是最后一个11');
    // _promise.then(res => {
    //     console.log('resolve2进入', res);
    // }, error => {
    //     console.log('reject2进入', error);
    // })

    // _promise.then(res=>{
    //     console.log('成功',res);
    // }).catch(err=>{
    //     console.log('catch',err);
    // }).finally(()=>{
    //     console.log('finally');
    // })