实现promise.all方法

实现promise.all方法,第1张

1- Promise.all 的用法

逆向的去实现功能,最关键的前提是准确了解API,输入、输出、和注意事项。

这里直接引用MDN:

Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。

MDN后面也给出了详细说明:

此方法在坦颂集合多个promise的返回结果时很有用。

完成(Fulfillment):

如果传入的可迭代对象为空,Promise.all会同步地返回一个已完成扮慎(resolved)状态的promise。

如果所有传入的promise都变为完成状态,或者传入的可迭代对象内没有promise,Promise.all返回的promise异步地变为完成。

在任何情况下,Promise.all返回的promise的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非promise值)。

失败/拒绝(Rejection):

如果传入的promise中有一个失败(rejected),Promise.all异步地将失败的那个结果给失败状态的回调函数,而不管其它promise是否完成。

个人感觉MDN解释的比较清楚了,还是云里雾里的话,可以反复细品一下上面的说明。或者结合下面的代码去理解。

2 - 手动实现Promise.all

面试美团的时候,面试官看我写不出来,就说“既然你知道了输入和输出是什么,应该能写出来了....”。

面试官其实不是在鄙视我“我不行”,而是在试图引导我的思路,只是当时自己编程思路太差,最后还是没写出来。

但是面试官的提示,确实是一个很好的思考思路。 先不管完整的Promise.all代码是什么样子,甚至包括优化啥的。先想想"Promise.all(iterable) 方法返回一个 Promise实例",就这么简单的一句话怎么写呢?

function myPromiseAll(arr) {  // 参数是一个iterable对象,一般是数组

  // 返回一个Promise实例

   return new Promise((resolve, reject) =>{

       resolve("面试官让我写一个Promise.all")

       // 或者

       // reject("我太笨了,写不出来")

       

   })

}

let pResult = myPromiseAll([]) // 先不要去想数组有没有元素

pResult.then(value=>{

   console.log(value) // 输出: 面试官让我写一个Promise.all

}, err=>{

   console.log(err)

})

好了,如过看懂了,那么最重要的一步就完成了。是不是很简单。

接下来,只要根据MDN的说明,一步步完善内部函数的功能就行了。

我们先从“完成”情况下手:

完成(Fulfillment):

A. 如果传入的可迭代对象为空,Promise.all会同步地返回一个已完成(resolved)状态的promise。

B. 如果所有传入的promise都变为完成状态,或者传入的可迭代对象内没有promise,Promise.all返回的promise异步地变为完成。

C. 在任何情况下,Promise.all返回的promise的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非promise值)。让缺郑

请先看C,在完成情况下,会始终返回一个数组.

function myPromiseAll(arr) {

   // 定义一个数组

   let result = []

   return new Promise((resolve, reject) =>{

       // 现在只考虑 “在完成情况下” ,会返回一个数组

       resolve(result)

     

       

   })

}

let pResult = myPromiseAll([])

pResult.then(value=>{

   console.log(pResult) // 输出 Promise { <state>: "fulfilled", <value>: [] }

   console.log(value)// 输出:[]

})

那么下面来实现B,B里有分两种情况:

元素是Promise实例

元素不是Promise实例

那先考虑元素不是Promise实例,从简单的开始

function myPromiseAll(arr) {

   let result = []

   return new Promise((resolve, reject) =>{

       for(let i = 0i <arr.lengthi++) {

           result.push(arr[i])

       }

       resolve(result)

     

       

   })

}

let pResult = myPromiseAll([1,2,3]) // 元素不是Promise实例

pResult.then(value=>{

   console.log(pResult)// 输出:  Promise { <state>: "fulfilled", <value>: (3) […] }

   console.log(value)// 输出: Array(3) [ 1, 2, 3 ]

})

最难的来了,元素都是Promise实例呢?

别慌,先写顶层设计,再想细节(自上向下编程)

function myPromiseAll(arr) {

   let result = []

   return new Promise((resolve, reject) =>{

       for(let i = 0i <arr.lengthi++) {

           

           if(/*如果是Promise实例*/) {

               

           } else {

               result.push(arr[i])

           }

       }

       // 先想想,resolve放在这里,对不对?

       resolve(result)

     

       

   })

}

继续完善

function myPromiseAll(arr) {

   let result = []

   return new Promise((resolve, reject) =>{

       // 数组为空,直接resolve了

       if(arr.length == 0) {

           resolve(result)

       }

       for(let i = 0i <arr.lengthi++) {

           

           if(arr[i].then) { // 若元素是Promise实例,则会有then函数,这里只是简单的作为判断标准

               

               // 元素是Promise

               arr[i].then(value =>{

                   console.log(value)

                   result.push(value)

                   // 想一想什么时候resolve呢?--- 所有Promise实例都完成了

                   if(result.length == arr.length) {

                      console.log("所有都完成了")

                       resolve(result)

                   }

               })

               

           } else {

               result.push(arr[i])

               // 这段代码跟上面重复,想想,能不能提取放到外面,会出现什么情况呢?

               if(result.length == arr.length) {

                   resolve(result)

               }

           }

       }

   })

}

let p1 = new Promise((resolve, reject)=>{

   setTimeout(resolve, 2000, "P1 resolved")

})

let p2 = new Promise((resolve, reject)=>{

   setTimeout(resolve, 3000, "P2 resolved")

})

let p3 = new Promise((resolve, reject)=>{

   setTimeout(resolve, 4000, "P3 resolved")

})

let pResult = myPromiseAll([p1,p2,p3])

pResult.then(value=>{

   console.log(pResult)

   console.log(value)

})

// 输出

// P1 resolved

// P2 resolved

// P3 resolved

// 所有都完成了

// Promise { <state>: "fulfilled", <value>: (3) […] }

// Array(3) [ "P1 resolved", "P2 resolved", "P3 resolved" ]

完成情况写完了,还剩失败情况:

如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成。

function myPromiseAll(arr) {

   let result = []

   return new Promise((resolve, reject) =>{

       // 如果数组为空,直接返回空数组

       if(arr.length == 0) {

           resolve(result)

       }

       for(let i = 0i <arr.lengthi++) {

           

           if(arr[i].then) { // 若元素是Promise实例,则会有then函数,这里只是简单的作为判断标准

               

               // 元素是Promise

               arr[i].then(value =>{

                   console.log(value)

                   result.push(value)

                   // 想一想什么时候resolve呢?

                   if(result.length == arr.length) {

                       console.log("所有都成功了")

                       resolve(result)

                   }

               }, err =>{

                   console.log("很不幸,其中一个失败了")

                   // 注意到没, 这里没有像上面的判断 result.length == arr.length, 为什么?

                   // 只要碰到 resolve 或 reject ,就结束了

                   reject(err)

               })

               

           } else {

               result.push(arr[i])

               // 这段代码跟上面重复,想想,能不能提取放到外面,会出现什么情况呢?

               if(result.length == arr.length) {

                   resolve(result)

               }

           }

       }

   })

}

let p1 = new Promise((resolve, reject)=>{

   setTimeout(reject, 2000, "P1 rejected")

})

let p2 = new Promise((resolve, reject)=>{

   setTimeout(resolve, 3000, "P2 resolved")

})

let p3 = new Promise((resolve, reject)=>{

   setTimeout(resolve, 4000, "P3 resolved")

})

let pResult = myPromiseAll([p1,p2,p3])

pResult.then(value=>{

   console.log(pResult) // 是输出成功

   console.log(value)

}, err =>{

   console.log(pResult)  // 还是输出失败呢?

   console.log(err)

})

// 输出

// 很不幸,其中一个失败了

// Promise { <state>: "rejected" }

// P1 rejected

// P2 resolved

// P3 resolved

为什么最后还是输出了 P2 和 P3 的结果呢? 这是因为,尽管遇到了P1就reject了,然而 P2 和 P3 仍在执行。注意MDN说的是“不管其他Promise是否完成”,而不是“其他Promise被stop”。

let p2 = new Promise((resolve, reject)=>{

   setTimeout(resolve, 3000, "P2 resolved")

})

let p3 = new Promise((resolve, reject)=>{

   setTimeout(resolve, 4000, "P3 resolved")

})

let pResult = myPromiseAll([p2,55,p3])

pResult.then(value=>{

   console.log(pResult)

   console.log(value)// 输出 [55, 'P2 resolved', 'P3 resolved']

}, err =>{

   console.log(pResult)

   console.log(err)

与 Promise.all 一样,参数是一组包含 Promise 实例的数组,返回值是一个凳袜或新的 Promise 实例,其实例在调用 then 方法中的回调函数的参数仍是一个数组。不同之处在于无论参数实例 resolve 还是 reject , Promise.allSettled 都会执行 then 方法的第一个回调函数(意思就是好模不会 catch 到参数实例的 reject 状态),其回调函数的参数返回的数组的每一项是一个包含 status 和 value 或者 reason 的一组对象。 status 代表对应的参数实例状态值,取值只有 fulfilled(resolve状态) 和 rejected(reject状态) ,当 status 的值为 rejected ,对应的另一个对象属性就是 reason 了,也就是被 reject 的原因,而成功返回的 status 的值则是 fulfilled ,对应的另枣伍一个对象属性便是 value ,对应的值就是 resolve 的任意值。

暂时只找到三种解决方案,如有补充,后续更新.

在实际项目中,可能会遇到 需要从前两个接口中的返回结果获取第三个接口的请求参数这种情况。 也就是需要等待两个/多个异步事件完成后,再进行回调。

对于异步回调,首先想到的就会是使用Promise封装运并,然后使用.then()来触发回调。那么对于两个或多个异步事旁枣迹件均完成后再触发回调可以使岩嫌用Promise.all()方法。

**Promise.all(iterable)** 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

摘自 官方说明


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12347271.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-24
下一篇 2023-05-24

发表评论

登录后才能评论

评论列表(0条)

保存