前端必刷手写题系列 [17]

273 阅读4分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

这个系列也没啥花头,就是来整平时面试的一些手写函数,考这些简单实现的好处是能看出基本编码水平,且占用时间不长,更全面地看出你的代码实力如何。一般不会出有很多边界条件的问题,那样面试时间不够用,考察不全面。

平时被考到的 api 如果不知道或不清楚,直接问面试官就行, api 怎么用这些 Google 下谁都能马上了解的知识也看不出水平。关键是在实现过程,和你的编码状态习惯思路清晰程度等。

注意是简单实现,不是完整实现,重要的是概念清晰实现思路清晰,建议先解释清楚概念 => 写用例 => 写伪代码 => 再实现具体功能,再优化,一步步来。

27. Promise.all

问题是什么

我们上一篇提到了一个Promise的 api Promise.all, 现在我们实现它试试

回顾下下 Promise.all()的用处

  • 接收一个可迭代的对象,例如Array,其中每个成员理论上都应该是Promise,数组中如有非Promise项,则此项当做成功
  • 如果所有Promise都成功,则返回成功结果数组
  • 如果有一个Promise失败,则返回这个失败结果

再来个测试用例就很清楚了

// 声明各种写法的 promise 异步函数
const pro1 = Promise.resolve('第1个 Promise (直接 resolve 的)')

const pro2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('第2个 Promise (包裹返回)')
  }, 2000)
})

const pro3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 3000, '第3个 Promise(带参数)')
})

const pro4 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('第4个 Promise 失败')
  }, 4000)
})

// 再来一个非Promise项 直接 number 100
const proPrimite = 100

// 都是成功的情况
const proAll = Promise.all([pro1, pro2, proPrimite, pro3]).then(res => {
  console.log(res)
}).catch(e => {
  console.log(e)
})
// [ '第1个 Promise (直接 resolve 的)',
//   '第2个 Promise (包裹返回)',
//   100,
//   '第3个 Promise(带参数)' ]

// 有失败的情况
const proAll2 = Promise.all([pro1, pro4, proPrimite]).then(res => {
  console.log(res)
}).catch(e => {
  console.log(e)
})
// 第4个 Promise 失败

当然你要是想优雅点,写个工厂来创建 promise,像上篇一样, 也行。

分析

明白了这个函数是做什么的,那么我们思考如何去实现它

首先要实现个函数,你要先清楚它的出入参,上面的例子很容易看出

Promise.all([pro1, pro4, proPrimite]).then(res => {}).catch(e => {})

我们观察到使用 Promise.all 时,用 then,表明它返回值是个 Promise

而入参是 接收一个可迭代的对象,例如Array

我们可以写出基本架子

const myPromiseAll = (iterableArr) => {
  return new Promise((resolve, reject) => {
    // ...
  })
} 

稍微做点鲁棒性,别啥参数都往里面传

const myPromiseAll = (iterableArr) => {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(iterableArr)) {
      return reject(new Error('params is not an iterable Array'))
    }
  })
} 

下面我们还有的线索是

  • 都成功返回成功结果数组
  • 有失败则返回这个失败结果

注意:这个的resolve回调执行是在所有输入的 promise 的 resolve 回调都结束,或者输入的iterableArr里没有promise了的时候。

所以我们需要创建一个结果数组来保存所有成功的 resolve 回调的结果, 并创建一个计数器来判断是否还有迭代元素, 然后就简单了 遍历数组执行回调,保存输出,看代码注释就行

const myPromiseAll = (iterableArr) => {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(iterableArr)) {
      return reject(new Error('params is not an iterator Array'))
    }
    // 我们可以创建一个结果数组来保存所有成功的 resolve 回调的结果
    let res = [], count = 0
    // 开始遍历每个迭代元素
    for (let i = 0; i < iterableArr.length; i++) {
      // 最上面第一个例子可以看出 直接Promise.resolve() 可传入基本类型
      Promise.resolve(iterableArr[i]).then(value => {
        // 成功resolve 回调的结果 推入 res 基本类型直接推入  res
        res.push(value)
        count++
        // 当所有resolve回调 都结束,Promise.all的这个 resolve 才会执行
        if (count === iterableArr.length) {
          resolve(res)
        }
      }).catch(e => {
        // 有错就直接 reject 了
        reject(e)
      })
    }
  })
} 

最后我们可以替换上面例子中的 Promise.all 尝试也能输出正确结果

const proAll = myPromiseAll([pro1, pro2, proPrimite, pro3]).then(res => {
  console.log(res)
}).catch(e => {
  console.log(e)
})

另外向大家着重推荐下另一个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列 记得点赞哈

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友 Or 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考