【题目】实现Promise.race(可以使用Promise)

94 阅读3分钟

实现一个Promise.race(可以使用Promise)

Promise.race的用法

阮一峰老师的教程:ECMAScript 6 入门 - Promise-race

Promise.race使用示例代码:

  const p1 = new Promise((resolve, reject) => {
    console.log("p1 new"); // 1. 
    setTimeout(() => {
      console.log("p1 setTimeout 300"); // 6.
      resolve("p1");
      // reject("p1 err");
    }, 300);
  });
  p1.name = "p1";
  console.log("after p1"); // 2. 
  const p2 = new Promise((resolve, reject) => {
    console.log("p2 new"); // 3.
    setTimeout(() => {
      console.log("p2 setTimeout 200"); // 5.
      resolve("p2");
      // reject("p2 err");
    }, 200);
  });
  p2.name = "p2";
  console.log("after p2"); // 4.
  Promise.race([p1, p2])
    .then((data) => {
      // 先resolve完成的,会进入这里
      console.log(data); // p2
    })
    .catch((err) => {
      // 先reject的,会进入这里
      console.error(err);
    });

Promise的状态

new Promise后如果没有执行resolve或者reject改变它的状态,那么它是pending状态

给Promise实例添加个name属性作为标识

// p0是一个Promise实例
const p0 = new Promise((resolve, reject) => {
    console.log("p0 new");
});
p0.name = "p0";
console.log(typeof p0); //  object
console.log(Object.prototype.toString.call(p0)); //  '[object Promise]'
console.log(p0);
// 打印p0 因为没有执行resolve或者reject改变它的状态,那么它是pending状态
// Promise {<pending>, name: 'p0'}

使用Promise实现Promise.race

参考资料:【手写 Promise 源码】第十二篇 - Promise.race 的实现

Promise.race 是Promise的静态方法,参数为数组,返回一个Promise实例。

在myRace函数中返回一个新的Promise实例pInstance,在new Promise的参数函数中循环promises数组,执行p.then(resolve, reject);,将当前pInstance的resolve, reject函数作为参数传过去。

p1 p2里哪个里面的异步先执行,对应传入的pInstance的resolve(成功状态)或reject(失败状态)函数就会执行。这两个函数作为参数传入了p1和p2的then方法,但最后只会执行一次。resolve的执行时机是取决于p中resolve的时机的,也就实现了race的功能。

  const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("p1");
      // reject("p1 err");
    }, 300);
  });
  p1.name = "p1";
  const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("p2");
      // reject("p2 err");
    }, 200);
  });
  p2.name = "p2";

  // promises 数组中有多个promise实例
  Promise.myRace = function (promises) {
    const pInstance = new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        let p = promises[i];
        console.log(p); // 是按照promises传入顺序打印的
        // p1和p2都执行then方法,并将当前pInstance的resolve, reject函数作为参数传过去
        // then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数
        // p1 p2里哪个里面的异步先执行,对应传入的pInstance的resolve(成功状态)或reject(失败状态)函数就会执行
        // 这个两个函数虽然作为参数传入了p1和p2的then方法,但最后只会执行一次
        // resolve的执行时机是取决于p中resolve的时机的
        // 也就实现了race的功能
        p.then(resolve, reject);
      }
    });
    return pInstance;
  };

  // 调用 Promise.myRace 返回了一个新的 Promise
  const newPromise = Promise.myRace([p1, p2]);
  newPromise
    .then((data) => {
      // 先resolve完成的,会进入这里
      console.log("newPromise data:", data);
    })
    .catch((err) => {
      // 先reject的,会进入这里
      console.error(err);
    });
  
  /*
  // p2先执行了,简化示意代码如下:
  const pInstance = new Promise((resolve, reject) => {
    // p2中的异步执行后,resolve("p2") 执行,然后会进入这里的resolve
    // 将数据作为参数给这里的resolve
    p2.then(resolve, reject);
  });
  // 最终数据会被then的第一个函数参数接收到
  pInstance.then((data) => {
    console.log(data);
  });
  */

优化Promise.myRace

增加对于判断传入的数组项的判断,如果不是Promise实例,直接执行resolve,并将该项作为resolve函数的参数。与Promise.race的行为一致。

Promise.myRace1 = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      let p = promises[i];
      // 增加判断p必须是Promise实例
      if (Object.prototype.toString.call(p) === "[object Promise]") {
        p.then(resolve, reject);
      } else {
        // 否则直接resolve传入的参数
        resolve(p);
      }
    }
  });
};

Promise.myRace1([p1, p2, "1"])
  .then((data) => {
    // 先resolve完成的,会进入这里
    // 直接打印字符串 '1'
    console.log("newPromise data:", data);
  })
  .catch((err) => {
    // 先reject的,会进入这里
    console.error(err);
  });