走进Promise-第二弹:then的链式调用(二)

723 阅读8分钟

在之前文章juejin.cn/post/692422…,写入了链式调用的基本功能,

那么我们知道,promise链式调用then方法时,可以返回一个普通值,也可以返回一个**promise对象**,

解决思路

  • 如果返回的是普通值,那么调用resolve方法把这个普通值传递给下一个promise对象即可
  • 如果返回的的是一个promise对象,那就需要判断这个promise的状态,根据状态决定调用什么方法,并把调用值传递给下个promise对象。

那么此时需要在**testPromise**中:

class testPromise {
  // 此出省略若干字,这里主要书写添加上述处理的代码
  then (successCallback, failCallback) {
    /**
     * 判断状态,并根据状态指定相应的回调函数
     * 那么成功后的值,失败后的值在哪呢,就是resolve和reject方法在调用后传递的值
     * 
     * 普通链式调用见下面1、2、3步
     */
    let promise2 = new testPromise ((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          // 2.保存上一次then方法回调函数的返回值
          let x = successCallback(this.value);
          // 3.将上一次回到函数的返回值作为参数,传递给下一个then方法调用
          // resolve(x);
          /**
           * 如果这里传入的是普通值,那么直接调用resolve方法,并把当前值传递给下一个promise对象
           * 如果传入的是promise对象,那么需要先判断下promise状态,根据状态决定调用resolve还是reject,
           * 并把相应的成功值或失败原因传给下一个promise对象
           * 
           * 这里如果是异步还有重复调用resolve或reject方法,那么就把这个方法单独写出来,即用即调
           * 此方法需要的参数即我们这里提到的三个:x、resolve、reject
           */

          resolvePromise(x, resolve, reject);
        }, 0)

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

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

         this.successValue.push(successCallback);
         this.failReason.push(failCallback);
        return;
      }
    })
    
    // 1.返回promise对象
    return promise2

  }
}

function resolvePromise(x, resolve, reject) {
  /**
   * 判断普通值
   * 方法:判断这个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)

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

// 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;
}

执行结果:

如果调用reject方法

代码说明

上面代码中主要在then方法中加入了对传入值的处理

如何判断是普通值还是promise对象?

x instanceof testPromise通过判断x是否是class类中的实例对象。

1.如果传入的是普通值,那么直接调用resolve方法,并将当前的值传递给下一个promise对象。
2.如果传入的是promise对象,如上面代码中other方法在第一个then方法中调用,返回promise对象,那么此时x即是promise对象,调用其then方法,根据当前状态,再决定是调用then方法中的成功回调函数还是失败回调函数。

那么此时你的手写promise应该是以下这样:

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

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

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

  /**
   * 初始状态为等待
   * 每个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()(this.value);
  }

  // 调用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()(this.reason);

  }

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

          /**
           * 如果这里传入的是普通值,那么直接调用resolve方法,并把当前值传递给下一个promise对象
           * 如果传入的是promise对象,那么需要先判断下promise状态,根据状态决定调用resolve还是reject,
           * 并把相应的成功值或失败原因传给下一个promise对象
           * 
           * 这里如果是异步还有重复调用resolve或reject方法,那么就把这个方法单独写出来,即用即调
           * 此方法需要的参数即我们这里提到的三个:x、resolve、reject
           */

          resolvePromise(x, resolve, reject);
        }, 0)

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

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

         this.successValue.push(successCallback);
         this.failReason.push(failCallback);
        return;
      }
    })
    
    // 1.返回promise对象
    return promise2

  }
}

function resolvePromise(x, resolve, reject) {
  /**
   * 判断普通值
   * 方法:判断这个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)

  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;
}