Promise详解:手写Promise底层-实现Promise所有的功能和方法

news2024/11/19 15:39:20

前言

	目标:
		封装一个promise,更好的理解promise底层逻辑
	需求:
		实现以下promise所有的功能和方法 如下图所示

在这里插入图片描述

一、构造函数编写

步骤

1、定义一个TestPromise类,
2、添加构造函数,
3、定义resolve/reject,
4、执行回调函数
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>手写Promise</title>
</head>

<body>
  <h2>构造函数</h2>
  <script>
    // 1. 定义类
    class TestPromise{
         // 2. 添加构造函数
        constructor(func){
            // 3、声明 resolve reject
            const resolve = (result)=>{
                // TODO
                console.log("resolve",result)
            }
            const reject = (result)=>{
                // TODO
                console.log("reject",result)
            }
             // 4. 执行回调函数
            func(resolve,reject)
        } 
    }
    // ------------- 测试代码 -------------
    const p = new TestPromise((resolve, reject) => {
        console.log("调用了")
        resolve('success')
        //   reject('error')
    })
  </script>
</body>

</html>

在这里插入图片描述

二、promise的状态和原因

分析

promise有pending->fulfilled pending->rejected,
所以我们要为我们的实例类添加状态以及导致状态变化的原因
state状态 result原因
而且当pending状态一旦发生变化,便不可逆

步骤

1、添加状态(pending / fulfilled / rejected)
2、添加原因
3、调整resolve/reject
4、状态不可逆
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>手写Promise</title>
</head>

<body>
  <h2>构造函数</h2>
  <script>
    // 通过变量保存状态,便于后续使用
    const PENDING = 'pending'
    const FULFILLED = 'fulfilled'
    const REJECTED = 'rejected'
    class TestPromise{
         // 1. 添加状态(pending / fulfilled / rejected)
         state = PENDING
         // 2. 添加原因
         result = undefined
        constructor(func){
            // 改状态: pending->fulfilled
            // 记录原因
            const resolve = (result)=>{
                // 加判断状态不可逆
                if(this.state===PENDING){
                    this.state = FULFILLED
                    this.result = result
                }
            }
            // 改状态: pending->rejected
            // 记录原因
            const reject = (result)=>{
                if(this.state===PENDING){
                    this.state = REJECTED
                    this.result = result
                }
            }
            func(resolve,reject)
        } 
    }
    // ------------- 测试代码 -------------
    const p = new TestPromise((resolve, reject) => {
        resolve("fulfilled")
        // 只会执行上面的 状态变成fulfilled后下面不再执行
        reject("rejected")
    })
  </script>
</body>

</html>

在这里插入图片描述

三、then方法

分析

promise有成功和失败回调,异步多次调用

步骤一:成功和失败回调

1、添加实例方法
2、参数判断,判断传入的是不是回调函数
3、根据状态执行不同的回调函数(成功or失败)

注意如果传入的不是函数,成功和失败的回调默认实现是不同的,以下是文档,我们参考文档实现
在这里插入图片描述

class TestPromise{
         ···
        constructor(func){
            ···
        }
        // 1、添加实例方法
        then(onFulfilled, onRejected){
            // 2、参数判断,判断传入的是不是回调函数
            // 是函数返回函数,不是返回原值
            onFulfilled = typeof onFulfilled === 'function'?onFulfilled:x=>x
            // 是函数返回函数,不是抛出
            onRejected = typeof onRejected === 'function'?onRejected:x=>{ throw x}
            // 3、根据状态执行不同的回调函数(成功or失败)
            if(this.state === FULFILLED){
                onFulfilled(this.result)
            }else if(this.state === REJECTED){
                onRejected(this.result)
            }
        }
    }

	// ------------- 测试代码 -------------
	···
	p.then(res=>{
        console.log('成功回调',res)
    }, err=>{
        console.log('失败回调',err)
    })

步骤二:异步和多次调用

1、定义实例属性(pending状态下保存then保存的回调函数)
2、执行保存的成功和失败回调
class TestPromise{
         ···
         // 1、对象数组保存成功和失败的回调函数{onFulfilled, onRejected} 
         // # 定义属性私有,只有内部可以访问到
         #handlers = [] 
        constructor(func){
            const resolve = (result)=>{
                if(this.state===PENDING){
                    this.state = FULFILLED
                    this.result = result
                    // 3、执行成功的回调
                    this.#handlers.forEach(({onFulfilled})=>{
                        onFulfilled(this.result)
                    })
                }
            }
            const reject = (result)=>{
                if(this.state===PENDING){
                    this.state = REJECTED
                    this.result = result
                    // 3、执行失败的回调
                    this.#handlers.forEach(({onRejected})=>{
                        onRejected(this.result)
                    })
                }
            }
            func(resolve,reject)
        }
        then(onFulfilled, onRejected){
            onFulfilled = typeof onFulfilled === 'function'?onFulfilled:x=>x
            onRejected = typeof onRejected === 'function'?onRejected:x=>{ throw x}
            if(this.state === FULFILLED){
                onFulfilled(this.result)
            }else if(this.state === REJECTED){
                onRejected(this.result)
            }else if(this.state === PENDING){
            	// 2、保存回调函数
                this.#handlers.push({onFulfilled, onRejected})
            }
        }
    }
    // ------------- 测试代码 -------------
    const p = new TestPromise((resolve, reject) => {
    	// 异步
        setTimeout(()=>{
            resolve("fulfilled")
            // reject("rejected")
        },2000)
    })
    p.then(res=>{
        console.log('then1',res)
    }, err=>{
        console.log('then1',err)
    })
    p.then(res=>{
        console.log('then2',res)
    }, err=>{
        console.log('then2',err)
    })
解析:当定义类存在setTimeout时,这时的state属性为pending,then执行了
(所以我们要在then方法中加保存当前回调函数,当倒计时结束,调取resolve时我们执行回调函数数组)

四、异步任务

在这里插入图片描述

分析

promise.then()里面执行的是异步任务
所以我们的promise中也要实现异步处理,实现{
	1、核心API
	2、函数封装
}
核心API,vue2中执行异步的API有Promise.then、MutationObserver、setImmediate、setTimeout
选用queueMicrotask、MutationObserver、setTimeout
queueMicrotask:直接执行一个异步任务(node11开始支持、支持新式浏览器、IE不支持)
MutationObserver:dom节点改变执行异步任务(IE11支持)
setTimeout都支持
// 使用
queueMicrotask((fun)=>{
	fun() // 回调函数直接异步执行
})
const obs = new MutationObserver(()=>{
	// ...
})
const divNode = document.createElement('div')
// 参数一:观察的dom节点 参数二:观察的选项 childList观察子节点
obs.observe(divNode, { childList: true }) // 检测子节点是否改变
divNode.innerText = 'tets' // 开始修改子节点
// 节点发生改变执行异步回调

基于核心API完成异步任务的函数封装

1、定义函数,接收一个回调函数
2、调用核心api(queueMicrotask,MutationObserver,setTimeout)
3、在我们的promise调用封装的函数

1、定义函数,接收一个回调函数

// 1、定义函数
    function runAsynctask(callback){
        // 2. 调用核心api(queueMicrotask,MutationObserver,setTimeout)
        if(typeof queueMicrotask === 'function'){
            queueMicrotask(callback)
        }else if(typeof MutationObserver === 'function'){
            const obs = new MutationObserver(callback)
            const divNode = document.createElement('div')
            obs.observe(divNode, { childList: true })
            // 不需要把节点添加到页面
            divNode.innerText = 'test'
        }else{
            setTimeout(callback,0)
        }
    }

2、在我们的promise调用封装的函数

	class TestPromise{
		···
        then(onFulfilled, onRejected){
            onFulfilled = typeof onFulfilled === 'function'?onFulfilled:x=>x
            onRejected = typeof onRejected === 'function'?onRejected:x=>{ throw x}
            if(this.state === FULFILLED){
+++             runAsynctask(()=>{
                    onFulfilled(this.result)
                })
            }else if(this.state === REJECTED){
+++             runAsynctask(()=>{
                    onRejected(this.result)
                })
            }else if(this.state === PENDING){
                this.#handlers.push({onFulfilled:()=>{
+++                 runAsynctask(()=>{
                        onFulfilled(this.result)
                    })
                }, onRejected:()=>{
+++                 runAsynctask(()=>{
                        onRejected(this.result)
                    })
                }})
            }
        }
    }
    // ------------- 测试代码 -------------
    console.log(1)
    const p = new TestPromise((resolve, reject) => {
        console.log(2)
        resolve(3)
        // reject("rejected")
    })
    p.then(res=>{
        console.log(res)
    })
    console.log(4)

五、链式编程

分析

promise.then().then()
promise可以一直then方法
核心:
1、then方法需要返回是支持.then调用的(promise实例)
2、根据 pending、fulfilled、rejected三种状态支持链式编程
3、在这个promise实例获取上一个then的返回值并处理
	{
		1、处理返回值
		2、处理异常
		3、处理返回promise
		4、处理重复引用
	}

1、处理返回值和处理异常

then方法中新建一个promise实例 获取返回值 处理异常
then(onFulfilled, onRejected){
     onFulfilled = typeof onFulfilled === 'function'?onFulfilled:x=>x
     onRejected = typeof onRejected === 'function'?onRejected:x=>{ throw x}
     // 1. 返回新Promise实例
     const p2 = new TestPromise((resolve, reject) => {
         if (this.state === FULFILLED) {
             runAsynctask(() => {
                 // 2. 获取返回值
                 try {
                     const x = onFulfilled(this.result)
                     //    2.1 处理返回值
                     resolve(x)
                 } catch (error) {
                     //    2.2 处理异常
                     console.log('捕获异常', error)
                     reject(error)
                 }
                 
                 
             })
         } else if (this.state === REJECTED) {
             runAsynctask(() => {
                 onRejected(this.result)
             })
         } else if(this.state === PENDING){
             this.#handlers.push({onFulfilled:()=>{
                 runAsynctask(()=>{
                     onFulfilled(this.result)
                 })
             }, onRejected:()=>{
                 runAsynctask(()=>{
                     onRejected(this.result)
                 })
             }})
         }
     })
     return p2
 }
 // ------------- 测试代码 -------------
    const p = new TestPromise((resolve, reject) => {
        resolve("resolve")
        // reject("rejected")
    })
    p.then(res=>{
        console.log(res)
        // throw 'throw error'
        return 2
    }).then(res=>{
        console.log(res)
    },err=>{
        console.log(err)
    })

2、处理返回promise

如果promise.then()里面返回依旧是一个promise,这个时候需要怎么处理?

const p = new TestPromise((resolve, reject) => {
      resolve(1)
    })
    p.then(res => {
      return new TestPromise((resolve, reject) => {
        resolve(2)
        // reject('error')
      })
    }).then(res => {
      console.log('p2:', res) // 2
    }, err => {
      console.log('p2:', err) // err
    })
处理思路:
1、拿到返回值、判断是不是promise实例
2、调去这个promise实例的then方法就可以了
class TestPromise{
		···
        then(onFulfilled, onRejected){
           ···
            const p2 = new TestPromise((resolve, reject) => {
                if (this.state === FULFILLED) {
                    runAsynctask(() => {
                        try {
                            const x = onFulfilled(this.result)
                            // 1.处理返回Promise
  +++                      if (x instanceof TestPromise) {
                                // 2. 调用then方法
                                // x.then(res => console.log(res), err => console.log(err))
+++                           x.then(res => resolve(res), err => reject(err))
                            } else {
                                resolve(x)
                            }
                        } catch (error) {
                            console.log('捕获异常', error)
                            reject(error)
                        }
                        
                        
                    })
                } else if (this.state === REJECTED) {
                    ···
                } else if(this.state === PENDING){
                    ···
                }
            })
            return p2
        }
    }

3、处理返回promise重复调用

 const p = new Promise((resolve, reject) => {
        resolve("resolve")
    })
    const p2 = p.then(res=>{
        // throw 'throw error'
       	return p2
    })
    p2.then(res=>{},err=>console.log('err:', err))

原生的promise会有重复调用的错误提示
在这里插入图片描述

思路:对promise进行比较、并抛出异常
if (this.state === FULFILLED) {
  runAsynctask(() => {
    try {
      const x = onFulfilled(this.result)
      // 1. 处理重复引用
      if (x === p2) {
        // console.log('返回了p2')
        // 2. 抛出错误 Chaining cycle detected for promise #<Promise>
        throw new TypeError('Chaining cycle detected for promise #<Promise>')
      }
      if (x instanceof TestPromise) {
        x.then(res => resolve(res), err => reject(err))
      } else {
        resolve(x)
      }
    } catch (error) {
      reject(error)
    }
  })

4、rejected状态

抽取公共方法处理promise和重复调用
//  抽取函数
    function resolvePromise(p2, x, resolve, reject) {
      if (x === p2) {
        throw new TypeError('Chaining cycle detected for promise #<Promise>')
      }
      if (x instanceof TestPromise) {
        x.then(res => resolve(res), err => reject(err))
      } else {
        resolve(x)
      }
    }

处理返回值

const p2 = new TestPromise((resolve, reject) => {
                if (this.state === FULFILLED) {
                    runAsynctask(() => {
                        try {
                            // 获取返回值
                            const x = onFulfilled(this.result)
                            resolvePromise(p2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                        
                        
                    })
                } else if (this.state === REJECTED) {
                    runAsynctask(() => {
                        // 1、处理异常
+++                     try {
                            // 获取返回值
+++                       const x = onRejected(this.result)
                            resolvePromise(p2, x, resolve, reject)
                        } catch (error) {
                            reject(error)
                        }
                    })
                }

5、pending状态

 } else if(this.state === PENDING){
       this.#handlers.push({onFulfilled:()=>{
           runAsynctask(()=>{
               try {
                   // 获取返回值
                   const x = onFulfilled(this.result)
                   resolvePromise(p2, x, resolve, reject)
               } catch (error) {
                   reject(error)
               } 
           })
       }, onRejected:()=>{
           runAsynctask(()=>{
               try {
                   // 获取返回值
                   const x = onRejected(this.result)
                   resolvePromise(p2, x, resolve, reject)
               } catch (error) {
                   reject(error)
               }
           })
       }})
   }

···
	// ------------- 测试代码 -------------
	const p = new TestPromise((resolve, reject) => {
        setTimeout(() => {
            resolve("resolve")
        }, 2000);
    })
    const p2 = p.then(res => {
        throw 'error'
        // return p2
        // return 2
        //   return new TestPromise((resolve, reject) => {
        //     reject('Promise-error')
        //   })
    })
    p2.then(res=>{
        return console.log('err:', err)
    }, err => {
      console.log('p2-err:', err)
    })

六、实例方法-catch -finally

实例方法-catch

我们先看下官网对catch的讲述
在这里插入图片描述
由此我们可以得出:

// catch是什么? 是语法糖!!!
new Promise(() => {
}).catch(() => {
})
// 等同于
new Promise(() => {
}).then(null, () => {
})
那么这个实力方法时处理以下两点
	1、then中没有写err函数
	const p = new TestPromise((resolve, reject) => {
      reject('reject-error')
    })
    p.then(res => {
      console.log('res:', res)
    }).catch(err => {
      console.log('err:', err)
    })


2、创建函数时的异常 没有resolve reject
const p = new TestPromise((resolve, reject) => {
      throw 'throw-error'
    })

实现:

1、在类then方法下面新建catch方法
/**
* catch方法
   * 1. 内部调用then方法
   * */
  catch(onRejected) {
    // 1. 内部调用then方法
    return this.then(undefined, onRejected)
  }
  2、在constructor构造函数中 处理异常
  constructor(func) {
        // pending->fulfilled
        const resolve = (result) => {
          if (this.state === PENDING) {
            this.state = FULFILLED
            this.result = result
            this.#handlers.forEach(({ onFulfilled }) => {
              onFulfilled(this.result)
            })
          }
        }

        // pending->rejected
        const reject = (result) => {
          if (this.state === PENDING) {
            this.state = REJECTED
            this.result = result
            this.#handlers.forEach(({ onRejected }) => {
              onRejected(this.result)
            })
          }
        }

        // 2. 处理异常
+++    try {
          func(resolve, reject)
        } catch (error) {
          // console.log('error:', error)
          reject(error)
        }
      }

实例方法-finally

以下时官网promise对finnally的介绍

在这里插入图片描述

在这里插入图片描述
由此可知,其内部调取then(onFinally, onFinally)

/**
* finally方法
 * 1. 内部调用then方法
 * */
finally(onFinally) {
    return this.then(onFinally, onFinally)
}

七、静态方法-resolve -reject-race-all-allSettled-any

注意:静态的是指向类自身,而不是指向实例对象,主要是归属不同,这是静态属性,静态方法的核心
也就是说类可以访问到,但实例对象访问不到、继承类也可以访问到

静态方法-resolve

官网介绍:
在这里插入图片描述
由此可知:resolve是把一个值转换为promise实例,如果本身就是promise实例那么直接返回
在这里插入图片描述
实现:

/**
  * 静态方法-resolve
  *  1. 判断传入值
  *  2.1 Promise直接返回
  *  2.2 转为Promise并返回(fulfilled状态)
  * */
 static resolve(value) {
     // 1. 判断传入值
     if (value instanceof TestPromise) {
         // 2.1 Promise直接返回
         return value
     }
     // 2.2 转为Promise并返回(fulfilled状态)
     return new TestPromise((resolve) => {
         resolve(value)
     })
 }

测试

 // ------------- 测试代码 手写Promise  -------------
    TestPromise.resolve(new TestPromise((resolve, reject) => {
      resolve('resolve')
      // reject('reject')
      // throw 'error'
    })).then(res => {
      console.log('res:', res)
    }, err => {
      console.log('err:', err)
    })

    TestPromise.resolve('hahah').then(res => {
      console.log(res)
    })

静态方法-reject

在这里插入图片描述
由此可知静态方法-reject为传递一个reject的promise对象,实现:

	/**
       * 静态方法-reject
       * 1. 返回rejected状态的Promise
       * */
      static reject(value) {
        // 1. 返回rejected状态的Promise
        return new TestPromise((undefined, reject) => {
          reject(value)
        })
      }
// 测试:
// ------------- 测试代码 手写Promise  -------------
    TestPromise.reject('error').catch(res => {
      console.log(res)
    })

静态方法-race

在这里插入图片描述
从官网可知,race接收一个promise对象数组,返回第一个最快执行完的promise,无论它是rejected还是fuilled
注意:如果传递的不是promise,会把值默认转换成promise对象,并执行resolve
注意:如果是传递的不是数组,会报以下错误
在这里插入图片描述

/**
 * 静态方法-race
  * 1. 返回Promise
  * 2. 判断是否为数组 错误信息:Argument is not iterable
  * 3. 等待第一个敲定
  * */
 static race(promises) {
     // 1. 返回Promise
     return new TestPromise((resolve, reject) => {
         // 2. 判断是否为数组
         if (!Array.isArray(promises)) {
             return reject(new TypeError('Argument is not iterable'))
         }
         // 3. 等待第一个敲定
         promises.forEach(p => {
             // p.then
             TestPromise.resolve(p).then(res => { resolve(res) }, err => { reject(err) })
         })
     })
 }
注意:因为返回时一个promise对象,所以 promises.forEach数组遍历中,
最快的一个会调用resolve或reject,
只要其中一个执行,promises对象便不会在执行其他的
// ------------- 测试代码 手写Promise  -------------
    const p1 = new TestPromise((resolve, reject) => {
      setTimeout(() => {
        resolve(1)
      }, 1000)
    })
    const p2 = new TestPromise((resolve, reject) => {
      setTimeout(() => {
        reject(2)
      }, 2000)
    })

    // TestPromise.race([p1, p2]).then((res) => {
      TestPromise.race([p1, p2, 'hahah']).then((res) => {
      // TestPromise.race().then((res) => {
      console.log('res:', res)
    }, err => {
      console.log('err:', err)
    })

静态方法-all

分析:
在这里插入图片描述

promise.all([promise1,promise2,promise3])
all方法接收一个promise数组,
如果都是resolve 并返回传入数组顺序的promise的res数组
当有rejected时,返回第一个rejected

思路:
在这里插入图片描述

 /**
  * 静态方法-all
  *  1. 返回Promise实例
  *  2. 判断是否为数组 错误信息:Argument is not iterable
  *  3. 空数组直接兑现
  *  4. 处理全部兑现
  *    4.1 记录结果
  *    4.2 判断全部兑现
  *  5. 处理第一个拒绝
  * */
 static all(promises){
     // 1. 返回Promise
     return new TestPromise((resolve, reject) => {
         // 2. 判断是否为数组
         if (!Array.isArray(promises)) {
             return reject(new TypeError('Argument is not iterable'))
         }
         // 3. 空数组直接兑现
         promises.length === 0 && resolve(promises)
         // 4. 处理全部兑现 
         // 思路(promise异步执行顺序,返回数组和原来的传入数组顺序可能不同索引 
         // 所以我们可以根据传入数组的索引值对最终数组进行数组排序)
         // 4.1 记录结果
         const results = []
         let count = 0
         promises.forEach((p, index)=>{
             TestPromise.resolve(p).then(res=>{
                 results[index] = res
                 // 4.2 判断全部兑现
                 // 为什么不能用 results.length进行判断,因为如果第一个执行完返回的是第三项results[3] = res 当前数组情况是[ , , res]
                 // 我们用count次数进行判断
                 count++
                 count===promises.length && resolve(results)


             }, err=>{
                 // 5. 处理第一个拒绝
                 reject(err)
             })
         })

         
     })
 }
 // ------------- 测试代码 手写Promise  -------------
 const p1 = TestPromise.resolve(1)
 const p2 = new TestPromise((resolve, reject) => {
   setTimeout(() => {
     resolve(2)
     // reject('error')
   }, 1000)
 })
 const p3 = 3
 const p4 = new TestPromise((resolve, reject) => {
   setTimeout(() => {
     // resolve(4)
     reject('error-1234')
   }, 2000)
 })

 TestPromise.all([p1, p2, p3, p4]).then(res => {
 // TestPromise.all().then(res => {
 //   TestPromise.all([]).then(res => {
   console.log('res:', res)
 }, err => {
   console.log('err:', err)
 })

静态方法-allSettled

分析:
在这里插入图片描述
我们promise测试一下
在这里插入图片描述

由此发现:它和all方法类似,依旧是等待所有的promise敲定返回,顺序也是传入顺序,
但不是第一个rejected抛出,而是全部执行完后以 status:fulfilled,value:value返回resolve,以status:rejected,reason:value返回rejected的数组

实现:
在这里插入图片描述

/**
    * 静态方法-allSettled
    * 1. 返回Promise
    * 2. 数组判断 错误信息: Argument is not iterable
    * 3. 为空直接敲定
    * 4. 等待全部敲定
    *  4.1 记录结果
    *  4.2 处理兑现{status:'fulfilled',value:''}
    *  4.3 处理拒绝{status:'rejected',reason:''}
    * */
 static allSettled(promises) {
   // 1. 返回Promise
   return new TestPromise((resolve, reject) => {
     // 2. 数组判断
     if (!Array.isArray(promises)) {
       return reject(new TypeError('Argument is not iterable'))
     }
     // 3. 为空直接敲定
     promises.length === 0 && resolve(promises)

     // 4. 等待全部敲定
     // 4.1 记录结果
     const results = []
     let count = 0
     promises.forEach((p, index) => {
       TestPromise.resolve(p).then(res => {
         // 4.2 处理兑现{status:'fulfilled',value:''}
         results[index] = { status: FULFILLED, value: res }
         count++
         count === promises.length && resolve(results)
       }, err => {
         // 4.3 处理拒绝{status:'rejected',reason:''}
         results[index] = { status: REJECTED, reason: err }
         count++
         count === promises.length && resolve(results)
       })
     })
   })
 }
// ------------- 测试代码 手写Promise -------------
 const p1 = TestPromise.resolve(1)
 const p2 = 2
 const p3 = new TestPromise((resolve, reject) => {
   setTimeout(() => {
     reject(3)
   }, 1000)
 })
 TestPromise.allSettled([p1, p2, p3]).then(res => {
   // TestPromise.allSettled().then(res => {
   // TestPromise.allSettled([]).then(res => {
   console.log('res:', res)
 }, err => {
   console.log('err:', err)
 })

静态方法-any

在这里插入图片描述
在这里插入图片描述

由此可知,any是接收一个promise数组(可以是常量),如果存在一个成功,则直接返回第一个成功的resolve
如果没有成功的,则返回所有的拒绝原因(与传入顺序一致)
注意:空值和空数组都会报错

实现
在这里插入图片描述

 /**
  * 静态方法-any
  * 1. 返回Promise,数组判断 错误信息: Argument is not iterable
  * 2. 空数组直接拒绝 AggregateError([错误原因1..],All promises were rejected)
  * 3. 等待结果
  *  3.1 第一个兑现
  *  3.2 全部拒绝
  */
 static any(promises) {
     // 1. 返回Promise,数组判断
     return new TestPromise((resolve, reject) => {
     if (!Array.isArray(promises)) {
         return reject(new TypeError('Argument is not iterable'))
     }
     // 2. 空数组直接拒绝
     promises.length === 0 && reject(new AggregateError(promises, 'All promises were rejected'))

     // 3. 等待结果
     const errors = []
     let count = 0
     promises.forEach((p, index) => {
         TestPromise.resolve(p).then(res => {
         // 3.1 第一个兑现
         resolve(res)
         }, err => {
         // 3.2 全部拒绝
         errors[index] = err
         count++
         count === promises.length && reject(new AggregateError(errors, 'All promises were rejected'))
         })
     })
     })
 }
// ------------- 测试代码 手写Promise -------------
 const p1 = new TestPromise((resolve, reject) => {
   setTimeout(() => {
     reject(1)
   }, 2000)
 })
 // const p2 = 2
 const p2 = TestPromise.reject(2)
 const p3 = new TestPromise((resolve, reject) => {
   setTimeout(() => {
     // resolve(3)
     reject(3)
   }, 1000)
 })

 TestPromise.any([p1, p2, p3]).then(res => {
   // TestPromise.any().then(res => {
   // TestPromise.any([]).then(res => {
   console.log('res:', res)
 }, err => {
   console.dir(err)
 })

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1120507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FL Studio21中文版本好用吗?值不值得下载

今天&#xff0c;我从一个FL Studio忠实且还算资深的用户角度&#xff0c;来为大家深度介绍并评测一下FL Studio的性能以及我四年的使用感受。 FL Studio是一款集剪辑、编曲、录音、混音一体的全能DAW&#xff08;数字音频工作站&#xff09;。其所有界面都是支持100%矢量化的…

Pycharm设置快捷键

本文主要讲一下Pycharm如何设置字体的缩小和放大的快捷键。 参见&#xff1a; 编程的快乐&#xff0c;你想象到了吗&#xff1f;PyCharm插件大全&#xff08;适合所有JetBrains家族产品&#xff09;_哔哩哔哩_bilibili

虚函数实例

1.声明&#xff1a;virtual 同名成员名 可实现父类访问子类中与其同名的成员 #include<iostream> using namespace std; class A{protected:int x;public:A(int x10):x(x1){}virtual void print(){//在类A中定义print为虚函数cout<<"A类中的x"<<x…

vue项目编译、打包、部署服务器运行

在vue项目执行npm run build,生成dis目录 打包dis上传 安装npm install -g http-server或者apt install node-http-server 运行http-server

微信小程序scroll-view设置display:flex后子view宽度设置无效解决

如果scroll-view设置了display:flex&#xff0c;子view设置宽度值无效&#xff0c;宽度值都是随着内容多少而改变&#xff1a; 效果和wxml&#xff1a; css: 原因&#xff1a;flex布局元素的子元素&#xff0c;自动获得了flex-shrink的属性 解决办法&#xff1a; 给子view增加…

国内智能客服机器人都有哪些?

随着人工智能技术的不断发展&#xff0c;智能客服机器人已经成为了企业客户服务的重要工具。国内的智能客服机器人市场也迎来了飞速发展&#xff0c;越来越多的企业开始采用智能客服机器人来提升客户服务效率和质量。 在这篇文章中&#xff0c;我将详细介绍国内知名的智能客服机…

大模型之Chat Markup Language

背景 在笔者应用大模型的场景中&#xff0c;对话模型(即大模型-chat系列)通常具有比较重要的地位&#xff0c;我们通常基于与大模型进行对话来获取我们希望理解的知识。然而大模型对话是依据何种数据格式来进行训练的&#xff0c;他们的数据为什么这么来进行组织&#xff0c;本…

7种典型的钢结构BIM应用

钢铁的工作流程往往会造成项目各个阶段信息缺乏、成本高、效率低等问题。 BIM技术通过数字化真实信息模拟建筑&#xff0c;通过中央文档共享信息&#xff0c;将流程的各个阶段紧密联系起来&#xff0c;交换信息&#xff0c;提高效率&#xff0c;降低成本。 制造专用软件不断发展…

pytorch C++ 移植

文章目录 前言安装 libtorch安装 opencv&#xff08;C&#xff09;模型转换通过跟踪转换为 Torch Script通过注解转换为 Torch Script 编写 C 代码编译环境搭建C 库管理方法一&#xff1a;手动配置 visual studio 环境方法二&#xff1a;cmake 配置环境 python 调用 C 程序 前言…

go语言Array 与 Slice

有的语言会把数组用作常用的基本的数据结构&#xff0c;比如 JavaScript&#xff0c;而 Golang 中的数组(Array)&#xff0c;更倾向定位于一种底层的数据结构&#xff0c;记录的是一段连续的内存空间数据。但是在 Go 语言中平时直接用数组的时候不多&#xff0c;大多数场景下我…

MySQL中查询重复字段的方法和步骤是怎样

示例 accountinfo 表数据如下&#xff1a; 场景一 单个字段重复数据查找 & 去重 我们要把上面这个表中 单个字段 account字段相同的数据找出来。 思路 分三步 简述&#xff1a; 第一步 要找出重复数据&#xff0c;我们首先想到的就是&#xff0c;既然是重复&#xff0c…

【斗破年番】再遭群嘲,美杜莎怀孕之事被魔改,三方联手除萧潇?

【侵权联系删除】【文/郑尔巴金】 斗破苍穹年番第67集已经更新了。和很多人一样&#xff0c;小郑也去看了&#xff0c;只是小郑万万没有想到&#xff0c;我满怀期待的去看这一集&#xff0c;这一集却能魔改成这样。魔改成什么样了呢&#xff1f;下面来分析下吧&#xff01; 一&…

高效表达三步

一、高效表达 高效表达定主题搭架子填素材 第一&#xff1a; 1个核心主题&#xff0c;让别人秒懂你的想法 &#xff08;表达要定主题&#xff09; 第二&#xff1a; 3种经典框架&#xff0c;帮你快速整理表达思路 第三&#xff1a; 2种表达素材&#xff0c;让发言更具说服力…

基础算法相关笔记

排序 最好情况下&#xff1a; 冒泡排序 最坏时间复杂度 O ( n 2 ) O(n^2) O(n2)。 插入排序 最坏时间复杂度为 O ( n 2 ) O(n^2) O(n2)&#xff0c;最优时间复杂度为 O ( n ) O(n) O(n)。 平均情况下&#xff1a; 快速排序 最坏时间复杂度为 O ( n 2 ) O(n^2) O(n2)&…

跟我一起写个虚拟机 .Net 7(四)- LC_3 解析实例

没想到这篇文章持续了这么久&#xff0c;越学越深&#xff0c;愣是又买了一本书《计算机系统概论》&#xff0c;当然&#xff0c;也看完了&#xff0c;受益匪浅。 系统化的学习才是正确的学习方式&#xff0c;我大学就没看到过这本书&#xff0c;如果早点看到&#xff0c;可能…

可视化 | python可视化相关库梳理(自用)| pandas | Matplotlib | Seaborn | Pyecharts | Plotly

文章目录 &#x1f4da;Plotly&#x1f407;堆叠柱状图&#x1f407;环形图&#x1f407;散点图&#x1f407;漏斗图&#x1f407;桑基图&#x1f407;金字塔图&#x1f407;气泡图&#x1f407;面积图⭐️快速作图工具&#xff1a;plotly.express&#x1f407;树形图&#x1f…

MySQL 排名函数 RANK, DENSE_RANK, ROW_NUMBER

文章目录 1 排名函数有哪些?2 SQL 代码实现2.1 RANK2.2 DENSE_RANK2.3 ROW_NUMBER 1 排名函数有哪些? RANK(): 并列跳跃排名, 并列即相同的值, 相同的值保留重复名次, 遇到下一个不同值时, 跳跃到总共的排名DENSE_RANK(): 并列连续排序, 并列即相同的值, 相同的值保留重复名…

图详解第六篇:多源最短路径--Floyd-Warshall算法(完结篇)

文章目录 多源最短路径--Floyd-Warshall算法1. 算法思想2. dist数组和pPath数组的变化3. 代码实现4. 测试观察5. 源码 前面的两篇文章我们学习了两个求解单源最短路径的算法——Dijkstra算法和Bellman-Ford算法 这两个算法都是用来求解图的单源最短路径的算法&#xff0c;区别在…

effective c++学习笔记(后四章)

六 继承与面向对象设计 红色字 \color{FF0000}{红色字} 红色字 32 确定你的public继承塑模出 is-a关系 如果你令class D (“Derived”)以public形式继承class B (“Base”)&#xff0c;你便是告诉C编译器&#xff08;以及你的代码读者&#xff09;说&#xff0c;每一个类型为…