走进Promise-第十弹:promise.race方法实现

1,260 阅读11分钟

promise.race方法

  1. race方法同all方法类似,同样是处理异步并发问题,不同的是race方法中只要有执行结束就进入回调,不管结果本身是成功状态还是失败状态。
  2. promise.race接收一个数组作为参数,包括普通值**promise**对象,执行结果就是哪个先执行完,哪个先进回调。
  3. promise.race方法的返回值也是一个promise对象,所以可以在promise.race后使用then方法。
  4. 使用promise.race的方式调用,所以race方法是一个静态方法。

解决思路:

  1. 声明静态方法race
  2. 返回一个promise对象,接收数组作为参数
  3. 循环数组,调用resolve方法,不管是普通值还是promise对象,都返回promise对象,然后调用then方法.
  4. 如果成功调用then中的成功回调,如果失败调用then中的失败回调,传入当前值,即有执行完的promise对象

testPromise.js中:

/**
   * race方法
   * 1.静态方法
   * 2.返回一个promise对象,接收数组作为参数
   * 3.循环数组,调用resolve方法,不管是普通值还是promise对象,都返回promise对象,然后调用then方法.
   * 4.如果成功调用then中的成功回调,如果失败调用then中的失败回调,传入当前值,即有执行完的promise对象
   */
  static race (array) {
    return new testPromise((resolve => {
      for(let i = 0; i < array.length; i++) {
        testPromise.resolve(array[i].then(value => resolve(value), reason => reject(reason)));
      }
    }))
  }

**index.js**中:

// 11.promise.race
const p1 = new testPromise((resolve, reject) => {
  setTimeout(() => resolve('p1'), 1000)
})
 
const p2 = new testPromise((resolve, reject) => {
  setTimeout(() => resolve('p2'), 5000)
})
 
testPromise.race([p1, p2]).then(ret => {
  console.log(ret) // 'p1'
})

执行结果:

至此,race方法实现。

到此,promise原理基本实现,下面是一部分总结:

  1. 是一个类。

  2. 调用的时候传递一个执行器,执行器接收两个参数,用于更改promise状态。

  3. promise对象拥有三种状态:pending(等待)、fulfilled(成功)、rejected(失败),状态一旦更改不可逆转。

  4. then方法对状态判断,如果成功就调用成功回调,如果失败就调用失败回调。

  5. then方法可链式调用,链式调用判断传入值是普通值还是promise对象

  6. 异步逻辑,promise对异步的处理。

  1. 执行器里面的代码异常;
  2. then方法成功回调,代码异常;
  3. then方法失败回调,代码异常;
  4. then方法异步逻辑成功回调,代码异常;
  5. then方法异步逻辑失败回调,代码异常;

常用静态方法的实现:

常用原型对象上的方法:

至此,完整**tesPromise.js**代码如下:

// 定义状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class testPromise {
  /**
   * 通过构造函数constructor接收传入的回调函数
   * 构造函数不能被直接调用,必须通过new运算符在创建对象时才会自动调用,
   * 当我们外部调用new myPromise()生成实例时,consctructor构造方法会执行,executor执行器立即执行
   *
   * executor代表执行器,会立即执行
   */

  constructor (executor) {
    try {
      // 执行器接收两个参数
      executor(this.resolve, this.reject); 
    } catch (error) {
      this.reject(error);
    }
    
  }

  /**
   * 初始状态为等待
   * 每个promise独有的
   */
  status = PENDING; 
  // 定义成功的值
  value = undefined;
  // 定义失败的值
  reason = undefined;
  // 定义异步中【等待】状态的存储成功回调函数
  // successValue = undefined;
  // 重复调用then,使用数组存储
  successValue = [];
  // 定义异步中【等待】状态的存储失败回调函数
  failReason = [];

  // 调用resolve后传递成功的值
  resolve = value => {
    // resolve作用是修改状态为成功且不可更改,修改状态之前需要判断当前状态是否为等待,如果不是就返回
    if (this.status !== PENDING) return;
    this.status = FULFILLED;
    // 保存成功之后的值
    this.value = value;
    // 处理异步,判断successValue是否有值,如果有就调用successCallback方法,并传入此时调用的值参数

    // console.log("successValue>>>", this.successValue); // [Function (anonymous)]

    /**
     * 1.此时的successValue存放的是successCallback这个回调函数,
     *   在调用2s后,执行resolve方法后,状态变为fulfilled,
     *   那么调用then方法,即调用成功后的回调函数,此时this.successValue就是这个回调函数
     *   if (this.successValue) this.successValue(this.value);
     * 
     * 2.重复调用then方法,那么就需要把存储在数组中的回调函数依次执行
     *   且倒叙排列,让第一个进入数组的先执行
     *   while (this.successValue.length) this.successValue.shift()(this.value);
     */
    while (this.successValue.length) this.successValue.shift()();
  }

  // 调用reject后传递失败的原因
  reject = reason => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    // 保存失败原因
    this.reason = reason;
    // 异步调用
    // if (this.failReason) this.failReason(this.reason);
    while (this.failReason.length) this.failReason.shift()();

  }

  /**
   * then方法
   * 判断状态
   * 定义在promise原型对象上的,
   * 作用是指定resolved状态的回调函数或者rejected状态的回调函数。
   * 也就是说resolve或reject方法更改promise的状态后,根据当前的状态,调用相应的回调函数
   */
  then (successCallback, failCallback) {
    successCallback = successCallback ? successCallback : value => value;
    failCallback = failCallback ? failCallback : reason => reason;
    /**
     * 判断状态,并根据状态指定相应的回调函数
     * 那么成功后的值,失败后的值在哪呢,就是resolve和reject方法在调用后传递的值
     * 
     * 普通链式调用见下面1、2、3步
     */
    let promise2 = new testPromise ((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            // 2.保存上一次then方法回调函数的返回值
            let x = successCallback(this.value);
            // 3.将上一次回到函数的返回值作为参数,传递给下一个then方法调用
            // resolve(x);

            /**
             * 如果这里传入的是普通值,那么直接调用resolve方法,并把当前值传递给下一个promise对象
             * 如果传入的是promise对象,那么需要先判断下promise状态,根据状态决定调用resolve还是reject,
             * 并把相应的成功值或失败原因传给下一个promise对象
             * 
             * 这里如果是异步还有重复调用resolve或reject方法,那么就把这个方法单独写出来,即用即调
             * 此方法需要的参数即我们这里提到的三个:x、resolve、reject
             * 
             * 传入promise2用于和x做判断,这里会有个问题就是,如果是异步执行那么这里是在then执行中传入,
             * 那么此时就会找不到这个promise2,所以这里调整为异步执行,在当前then方法执行完后再传入返回的promise2
             */

            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            // 这里调用的是下一个then方法的reject方法,而不是this.reject
            reject(error);
          }
          
        }, 0)

      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let y = failCallback(this.reason);
            reject(y);
          } catch (error) {
            reject(error);
          }
        }, 0)
        
      } else {
        /**
         * 在此处理异步情况,因为如果异步调用,此时promise状态为等待
         * 此时调用不了成功或失败,因为不知道是成功还是失败
         * 那么就需要把成功回调和失败回调存储,调用2s结束后,再调用相应的回调
         */
        // 存储成功or失败的值,存储后就需要再2s后调用resolve方法,那么就需要在resolve方法中判断是否有successCallback
        // this.successValue = successCallback;
        // this.failReason = failCallback;

        /**
         * then方法重复调用,非链式调用
         * 如果是同步,那么就有明确的状态,即走上面的fulfilled判断或者rejected判断即可
         * 如果是异步,就需要存储这些异步的回调,在2s后依次执行
         * 多个then方法调用,就需要使用数组存储
         */

        // try..catch中无法处理,所以改为push一个方法进去,在这个方法对回调函数进行异常捕获
        //  this.successValue.push(successCallback);
        //  this.failReason.push(failCallback);

        this.successValue.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
            
          }, 0)
        });
        this.failReason.push(() => {
          setTimeout(() => {
            try {
              let y = failCallback(this.reason);
              reject(y);
            } catch (error) {
              reject(error);
            }
          }, 0)
        });
        return;
      }
    })
    
    // 1.返回promise对象
    return promise2

  }

  /**
   * all方法
   * 静态方法使用static关键字声明
   * 1.接收数组作为参数,这里定义形参为array
   * 2.返回的对象是promise对象
   * 3.循环数组,判断当前的传入值是普通值还是promise对象,如果是普通值直接调用resolve方法返回
   * 4.如何判断,即判断当前传入值是否在这个testPromsie类中
   * 5.且需要一个数组接收处理后的promise对象
   */
  static all (array) {
    let result = [];
    let index = 0;
  
    return new testPromise((resolve, reject) => {
      // 定义方法将promise放到result数组
      function addData (key, value) {
        result[key] = value;
        // 对异步逻辑进行处理
        index ++;
        if (index === array.length) {
          resolve(result);
        }
      }

      for (let i = 0; i < array.length; i++) {
        // array[i]是否是testPromise的一个实例
        if (array[i] instanceof testPromise) {
          // promise对象
          array[i].then(value => addData(i, value), reason => reject(reason));
        } else {
          // 普通值
          addData(i, array[i])
        }
      }

      // 这里循环完立即执行,异步逻辑就不会处理,会导致这个异步没有执行
      // resolve(result);
    })

  }

  /**
   * race方法
   * 1.静态方法
   * 2.返回一个promise对象,接收数组作为参数
   * 3.循环数组,调用resolve方法,不管是普通值还是promise对象,都返回promise对象,然后调用then方法.
   * 4.如果成功调用then中的成功回调,如果失败调用then中的失败回调,传入当前值,即有执行完的promise对象
   */
  static race (array) {
    return new testPromise((resolve => {
      for(let i = 0; i < array.length; i++) {
        testPromise.resolve(array[i].then(value => resolve(value), reason => reject(reason)));
      }
    }))
  }

  /**
   * resolve方法
   * 同样为静态方法
   * 1.将给定值返回一个promise对象
   * 2.返回的promise对象中包裹这个给定值
   * 3.如果给定值是promise对象 直接返回
   * 4.如果给定值是普通值,返回一个promise对象,包裹这个值
   * 5.异步也可以执行
   */
  static resolve (value) {
    if (value instanceof testPromise) return value;
    return new testPromise((resolve, reject) => { resolve(value) })
  }

  /**
   * 无论当前的promise的状态是成功还是失败,finally方法的回调函数始终会被执行一次
   * finally方法可使用then方法链式调用
   * finally不是静态方法
   */
  finally(callback) {
    return this.then((value) => {
      return testPromise.resolve(callback().then(() => value));
      // 直接return value异步代码不会执行
      // return value;
    }, reason => {
      return testPromise.resolve(callback().then(() => {throw reason}));
    })
  }
  /**
   * catch
   */
  catch (failCallback) {
    return this.then(undefined, failCallback)
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  // console.log(promise2 === x);
  // 拦截循环调用then方法返回的promise对象
  if (promise2 === x) {
    return reject(new TypeError("Chaining cycle detected for promise #<Promise>"));
  }


  /**
   * 判断普通值
   * 方法:判断这个x是不是属于testPromise类的一个实例即可
   */
  if (x instanceof testPromise) {
    /**
     * promise对象,需要调用这个promise对象的then方法,
     * 那么then方法前面提过,接收成功和失败的回调函数作为参数,并把相应的value和reason传递下去
     */
    // x.then(value => resolve(value), reason => reject(reason))

    // 可简化为
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x)
  }
}


module.exports = testPromise;

index.js测试代码如下:

const testPromise = require ("./test-promise");

let promise = new testPromise ((resolve, reject) => {
  // 异步调用
  // setTimeout(() => {
  //   resolve("成功 .....");
  // }, 2000)

  // 6.1测试执行器发生错误
  // throw new Error ("executor error");

  resolve("成功");
  // reject("失败");
})

// 1.基础调用
// promise.then(value => {
//   console.log(value);
// }, reason => {
//   console.log(reason);
// })

// 2.链式调用
// promise.then(value => {
//   console.log("首次调用>>>", value);
//   return 100;
// }, reason => {
//   console.log("首次调用失败>>>", reason);
//   return 200;
// }).then(value => {
//   console.log("链式调用打印>>>", value);
// }, reason => {
//   console.log("链式调用失败>>>", reason);
// })

// 3.then方法的多次调用,非链式调用
// promise.then(value => {
//   console.log(value);
// }, reason => {
//   console.log(reason);
// })

// promise.then(value => {
//   console.log(value);
// }, reason => {
//   console.log(reason);
// })

// promise.then(value => {
//   console.log(value);
// }, reason => {
//   console.log(reason);
// })

// 4.链式调用then方法,传递普通值和传递配promise对象的处理
// promise.then(value => {
//   console.log("首次调用>>>", value);
//   return other ();
// }, reason => {
//   console.log("首次调用失败>>>", reason);
//   return 200;
// }).then(value => {
//   console.log("链式调用打印>>>", value);
// }, reason => {
//   console.log("链式调用失败>>>", reason);
// })

// function other () {
//   let otherPromise = new testPromise((resolve, reject) => {
//     // resolve("返回promise对象");
//     reject("reject调用测试");
//   })
//   return otherPromise;
// }

// // 5.拦截then方法中循环调用回调的then方法
// var p1 = promise.then(function (value) {
//   return p1;
// });

// p1.then(value => {
//   console.log("成功的回调>>>", value);
// }, reason => {
//   console.log("失败的回调>>>", reason);
// })

// 6.1测试执行器发生错误
// promise.then(value => {
//   console.log(value);
// }, reason => {
//   console.log("捕获【执行器】错误>>>", reason);
// })

// 6.2 then方法的成功回调函数 异常
// promise.then(value => {
//   // then方法成功回调中 异常 传递给下个
//   throw new Error ("successCallback error");
// })
// // 在第二个then方法捕获
// .then(value => {}, reason => {
//   console.log("捕获【then方法成功回调】错误>>>", reason);
// })

// 6.3 then方法的失败回调函数 异常
// promise.then(value => {
// },reason => {
//   throw new Error ("failCallback error");
// })
// // 在第二个then方法捕获
// .then(value => {}, reason => {
//   console.log("捕获【then方法失败回调】错误>>>", reason);
// })


// 6.4 then方法的异步成功回调函数 异常 && 6.5 then方法的异步失败回调函数 异常
// promise.then(value => {
//   throw new Error ("异步 error");
// },reason => {
//   console.log("捕获【then方法异步回调】错误111>>>", reason);
// })
// // 在第二个then方法捕获
// .then(value => {}, reason => {
//   console.log("捕获【then方法异步回调】错误>>>", reason);
// })

// 6.then方法可选参数链式调用
// promise
// .then()
// .then()
// .then()
// .then(value => console.log("可选参数执行结果>>>", value))

// // 7.promise.all方法
// function p1 () {
//   return new testPromise(function (resolve, reject) {
//     setTimeout(() => {
//       resolve("p1")
//     }, 2000);
//   })
// }

// function p2 () {
//   return new testPromise(function (resolve, reject) {
//     resolve("p2")
//   })
// }
// testPromise.all(['a', 'b', p1(), p2(), 'c'])
//   .then(result => {
//     console.log(result);
//   })

// 8.promise.resolve方法
// function p1 () {
//   return new testPromise(function (resolve, reject) {
//     resolve("p1")
//   })
// }

// 将给定的值转为promise对象,返回的promise对象包裹这个给定的值
// testPromise.resolve(10).tsthen(value => console.log(value));

// // 9.promise.finally方法
// function p1 () {
//   return new testPromise(function (resolve, reject) {
//     setTimeout(() => {
//       resolve("p1")
//     }, 2000);
//   })
// }
// function p2 () {
//   return new testPromise(function (resolve, reject) {
//     // reject("失败")
//     resolve("p2 成功")
//   })
// }

// p2().finally(() => {
//   console.log("finally");
//   return p1();
// }).then(value => {
//   console.log(value);
// }, reason => {
//   console.log(reason);
// })


// 10.catch方法
// function p2 () {
//   return new testPromise(function (resolve, reject) {
//     reject('失败')
//     // resolve('成功');  
//   })
// }

// p2()
//   .then(value => console.log(value))
//   .catch(reason => console.log(reason))


// 11.promise.race
const p1 = new testPromise((resolve, reject) => {
  setTimeout(() => resolve('p1'), 1000)
})
 
const p2 = new testPromise((resolve, reject) => {
  setTimeout(() => resolve('p2'), 5000)
})
 
testPromise.race([p1, p2]).then(ret => {
  console.log(ret) // 'p1'
})