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

499 阅读5分钟

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

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

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

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

30. Promise.race

分析

我们之前提到了一个Promise的 api Promise.all, 现在我们实现下 Promise.race()

了解下Promise.race()的用处

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个 promise解决或拒绝,返回的 promise 就会解决或拒绝。

语法

Promise.race(iterable);

参数 - iterable 可迭代对象,类似Array, 跟 Promise.all 一样

返回值 - 一个待定的 Promise 只要给定的迭代中的一个promise解决或拒绝,就采用第一个promise的值作为它的值,从而异步地解析或拒绝(一旦堆栈为空)。

描述

  • race 函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个

  • 如果传的迭代是的,则返回的 promise 将永远等待

  • 如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值

看个例子:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

const promise3 = new Promise((resolve, reject) => {
  setTimeout(reject, 300, 'three');
});

Promise.race([promise1, promise2, promise3]).then((value) => {
  console.log(value);
});
// two
// 打印 two 因为 promise2 最快执行完毕

Promise.race([promise1, promise3]).then((value) => {
  // 未被调用
  console.log(value);
}).catch(reason => {
  console.log(reason, 'is reject');
});
// three is reject
// 最快的是拒绝的 promise3, 所以走了 catch

在实现这个方法之前,建议看下这篇 手写Promise.all

手写实现

const myPromiseRace = (iterableArr) => {
  // 返回一个 Promise
  return new Promise((resolve, reject) => {
    if (!Array.isArray(iterableArr)) {
      return reject(new Error('params is not an iterator Array'))
    }
    // 开始遍历每个迭代元素
    for (item of iterableArr) {
      Promise.resolve(item).then((res) => {
        // promise 数组只要有任何一个 promise状态变更,就返回
        resolve(res)
      }).catch(e => {
        reject(e)
      })
    }
  })
} 

// 同样用我们的测试用例来实验下

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});

const promise3 = new Promise((resolve, reject) => {
  setTimeout(reject, 300, 'three');
});

myPromiseRace([promise1, promise2, promise3]).then((value) => {
  console.log(value);
});
// two

myPromiseRace([promise1, promise3]).then((value) => {
  console.log(value);
}).catch(reason => {
  console.log(reason, 'is reject');
});
// three is reject

没有问题,之后我们会利用这个 Promise.race限制异步的并发数量

31. Promise.allSettled

分析

我们在这篇中介绍了 Promise.allSettled

简单来说 Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。

我们在之前的一篇文章中已经介绍过,当我们使用 Promise.all() 执行过个 promise 时,只要其中任何一个promise 失败都会执行 reject ,并且 reject 的是第一个抛出的错误信息,只有所有的 promise 都 resolve 时才会调用 .then 中的成功回调

我们直接看实现, 很像 Promise.all

手写实现

先写测试用例, 建立 4 个 promise, 第四个是失败的。

// 先建立一个工厂函数, 入参是名字和异步时间
const promiseFactory = (name, wait, isFail = false) => {
  return new Promise((resolve, reject) => {
    // 异步任务(用 setTimeout 模拟)
    setTimeout(() => {
      if (!isFail) {
        resolve(`我是 ${name},我需要 ${wait} ms, 执行成功`)
      } else {
        reject(`我是 ${name},我需要 ${wait} ms, 执行失败`)
      }
    }, wait)
  })
}

let pro1 = promiseFactory('第一个异步任务', 3000)
let pro2 = promiseFactory('第二个异步任务', 1000)
let pro3 = promiseFactory('第三个异步任务', 2000)
let pro4 = promiseFactory('第四个异步任务', 1500, true)

然后实现

const myPromiseAllSettled = (iterableArr) => {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(iterableArr)) {
      return reject(new Error('params is not an iterator Array'))
    }
    let res = [], count = 0
    for (let i = 0; i < iterableArr.length; i++) {
      Promise.resolve(iterableArr[i]).then(value => {
        res[i] = {status: "fulfilled", value: value}
      }).catch(err => {
        res[i] = {status: "rejected", reason: err}
      }).finally(() => {
        // 每次都计数,直到全部执行完,返回结果
        count++
        if (count === iterableArr.length) {
          resolve(res)
        }
      })
    }
  })
}

myPromiseAllSettled([pro2, pro1, pro3, pro4]).then(res => {
  console.log(res) 
}, err => {
  console.log(err)
})

// 3秒后打印结果, 不管成功失败,都会返回
// [ 
//   {status: "fulfilled", value: "我是 第二个异步任务,我需要 1000 ms, 执行成功"},
//   {status: "fulfilled", value: "我是 第一个异步任务,我需要 3000 ms, 执行成功"},
//   {status: "fulfilled", value: "我是 第三个异步任务,我需要 2000 ms, 执行成功"},
//   {status: "rejected", reason: "我是 第四个异步任务,我需要 1500 ms, 执行失败"} 
// ]

这样也就成功实现了。

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

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

参考