手写promise常用方法

150 阅读3分钟

闲聊

距离第一篇博客迭代器已经过去了一个学期,当时就说下一篇文章想要写 promise 和生成器。没想到鸽了这么久,最近到底在干嘛呢?看了几个月的 vue 源码和 js 高级,但是收效甚微,我老是喜欢做一些高投入低回报的东西,如果当年专一搞前端,或者说专心搞一个框架,会不会早就找到实习了呢?唉话不多说,接下来只要有时间必然更新 js 高级手写,vue 源码等等,数组所有 api、Object、promise、generator、async await、axios 源码手写都会写。

正文

基本介绍

promise 是解决回调地狱的最佳方案,js 虽然是单线程,但是对于耗时较久的任务有一套自己的解决方案,就是通过 Event Loop 来异步地执行一些任务,从而达到非阻塞的目的。但是异步的缺陷就在于处处充满回调函数,而且异步任务的完成先后没有定律,想要异步任务同步执行,就必须采用回调嵌套回调的形式(如下代码),一旦使用过多必然造成代码不好维护。因此 promise 重要性可想而知,js 中凡是涉及到异步的都是 js 高级的内容,同样都是面试的重点,也是,毕竟将异步作为特色的语言 js 是当之无愧的老大哥。

//回调地狱
fs.readFile('1.txt',(err,data)=>{
    fs.readFile('2.txt',(err,data)){
        fs.readFile('3.txt',(err,data)=>{
            console.log('3.txt');
        })
    }
})

手写 Promise.all

首先了解 Promise.all 方法的作用,Promise.all 会等待所有的 Promise 结果返回,所有 promise 成功才算成功,一旦存在 Promise 失败就算失败,应用场景也挺多的,有时候针对多个请求需要同时成功才算成功,或者全部成功才继续进行请求时就会用到。

Promise._all = function (promises) {
  return new Promise((resolve, reject) => {
    let results = [];
    let count = 0;
    promises.forEach((p, i) => {
      p.then((res) => {
        results[i] = res;
        if (++count == promises.length) {
          // 为了确保位置不变,不能简单的push
          resolve(results);
        }
      }).catch((err) => {
        reject(err);
      });
    });
  });
};
Promise._all([1, 2, 3]).then((res) => {
  console.log(res);
});
// 打印[1,2,3]

可以看到用到了 count 计数,其实最开始手写我都是直接 push 进数组,后来和 Google 浏览器的 all 一对比发现,我的 promise 打印顺序和执行完成时机有关,不是严格的和传递的参数位置有关。Promise.all([1,2,3])应该打印严格[1,2,3](只是演示,1,2,3 是异步才能看到效果)

手写 Promise.any

和 Promise.all 相对,只要一个 Promise 成功就算成功,所有 Promise 失败才算失败。所以我们只需要把计数的 count 放到 catch 中即可。按照 Google 浏览器的 any 方法,所有都失败时打印"All promises were rejected"

Promise._any = function (promises) {
  let count = 0;
  return new Promise((resolve, reject) => {
    promises.forEach((p) => {
      p.then((res) => {
        resolve(res);
      }).catch((err) => {
        if (++count == promises.length) {
          reject("All promises were rejected");
        }
      });
    });
  });
};

手写 Promise.race

见名知意,race 就是所有的 promise 竞争,最终的 promise 结果就以最快完成的为准,不论成功或失败。

Promise._race = function (promises) {
  return new Promise((resolve, reject) => {
    promises.forEach((p) => {
      p.then((res) => {
        resolve(res);
      }).catch((err) => {
        reject(err);
      });
    });
  });
};

手写 Promise.allSettled

allSettled会接收所有的promise结果,不论是成功或者失败,Google 浏览器实现的 Promise.allSettled 打印结果也是数组,但是相对于 Promise.all 而言,数组的元素都是对象,如果成功返回成功状态和成功的值,如果失败返回失败状态和失败的原因。这一点也好理解,毕竟allSettled会接受所有的结果,不论成功或失败。

Promise._allSettled = function (promises) {
  return new Promise((resolve, reject) => {
    let count = 0;
    let results = [];
    promises.forEach((p, i) => {
      p.then((res) => {
        results[i] = {
          status: "fullfiled",
          value: res,
        };
        if (++count == promises.length) {
          resolve(results);
        }
      }).catch((err) => {
        results[i] = {
          status: "rejected",
          reason: err,
        };
        if (++count == promises.length) {
          resolve(err);
        }
      });
    });
  });
};

最后

✌,又成功水了一篇文章(开玩笑),确实文笔不行,只会埋头敲代码。2023 新的一年希望可以找到好的实习,比赛拿奖,学习顺顺利利吧,读研方面还是希望有机会的。
下一篇,手写promise。