走进Promise-第一弹:promise的基础功能

343 阅读13分钟

以下内容,需对promise有基本了解,这里不再赘述,这里重在看下promise内部实现原理。

需要index.js文件模拟调用promisetestPromise自定义promise方法。

promise的基础功能:

  1. promise是一个类,使用时需要传入一个回调函数,这个回调函数称为执行器,当使用new promsie()创建实例对象时,执行器会立即执行。

    index.js中,传入的(resolve, reject)...是需要立即执行的,所以在testPromise中使用constructor接收,并立即执行。

    // index.js中,传入的(resolve, reject)...是需要立即执行的,所以在
    let promise = new testPromise((resolve, reject) => {
      resolve("成功");
      reject("失败");
    )
    promise.then(() => {}, () => {})
    

    testPromise.js

    class testPromise {  constructor(excutor) {
        excutor() // excutor代表执行器,调用new关键字声明实例对象时,会立即执行
      }
    }
    
  2. promise的执行器接收两个参数,resolvereject,这个两个参数是函数,这个两个函数的作用就是更改promise的状态。
    testPromise.js

    class testPromise {  constructor(excutor) {
        excutor(resolve, reject) // 执行器接收两个参数,这两个参数是函数
      }
    }
    

    这里为什么用construtor接收回调函数?
    constructor是ES6类中的构造函数,当使用new关键字声明实例对象时,会立即执行,同时返回实例对象this。如果没有显式定义constructor,也会默认有一个构造函数。
    等同于es5通过构造函数方法:

    function testPromise (excutor) {  excutor() {}
    }
    
  3. promise有三种状态:pending(等待)、fulfilled(成功)、rejected(失败)
    pending => fulfilledpending => rejected,状态一旦确定就不会再发生变化。
    上面回调函数的两个参数,就是分别完成这两个过程。
    index.js中引入

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

    testPromise.js

    // 定义状态
    const PENDING = "pending";
    const FULFILLED = "fulfilled";
    const REJECTED = "rejected";
    class testPromise {  constructor(excutor) {
        excutor(this.resolve, this.reject) {} // 在类中调用这两个函数,改用this,这里的this指向实例对象
        // 定义为箭头函数,是为了让当前this指向promise对象,因为实例化调用直接使用resolve(),那么this指向会指向调用它的对象或者undefined
    
        // 每个promise的私有属性status,定义初始值为等待
        status = PENDING;
        // 成功
        resolve = () => {
          // resolve作用是修改状态为成功且不可更改,修改状态之前需要判断当前状态是否为等待,如果不是就返回
          if (this.status !== PENDING) return;
          this.status = FULFILLED;
        } 
    
        // 失败
        reject = () => {
    if (this.status !== PENDING) return;      this.status = REJECTED;    } 
      }
    }
    
    // 导出
    module.exports = testPromise;
    
  4. then方法是对状态判断,如果成功就调用成功的方法,如果失败就调用失败的方法。
    then方法是定义在promise原型对象上的,作用是指定resolved状态的回调函数或者rejected状态的回调函数。也就是说resolvereject方法更改promise的状态后,根据当前的状态,调用相应的回调函数

    // 在index.js中
    let promise = new testPromise((resolve, reject) => {  resolve("成功");  reject("失败");
    )
    promise.then(() => {}, () => {})
    
    // 在testPromise.js中 添加then方法的调用// 定义状态
    const PENDING = "pending";
    const FULFILLED = "fulfilled";
    const REJECTED = "rejected";
    
    class testPromise {  
    .... // 上面代码暂省略   
    
        /**
       * then方法
       * 判断状态
       * 定义在promise原型对象上的,
       * 作用是指定resolved状态的回调函数或者rejected状态的回调函数。
       * 也就是说resolve或reject方法更改promise的状态后,根据当前的状态,调用相应的回调函数
       */
      then (successCallback, failCallback) {
        // 判断状态,并根据状态指定相应的回调函数
        if (this.status === FULFILLED) {
          successCallback()
        } else if (this.status === REJECTED) {
          failCallback()
        } else {
          return;
        }
    
      }
    }
    
  5. then成功回调一个参数,表示成功之后的值,then失败回调一个参数,表示失败之后的原因。
    那么当调用then方法传入的参数即表示成功的返回值,或者失败的返回值

    // index.js中
    promise.then(result => {}, reason => {})
    
    // testPromise.js中,在第4点添加返回值
    class testPromise {  .... // 上面代码暂省略  
    
      then (successCallback, failCallback) {
        // 判断状态,并根据状态指定相应的回调函数
        if (this.status === FULFILLED) {
          successCallback(this.value) 
        } else if (this.status === REJECTED) {
          failCallback(this.reason) 
        } else {
          return;
        }
    
      }
    }
    
  6. then方法可以被链式调用,后面then方法的回调函数拿到的值是上一个then方法的回调函数的返回值,这个返回值是一个promise对象。

  • 首先链式调用就需要在上一次then调用后,返回一个新的promise对象,用于下一次的then方法调用。

  • 其次就是返回promise,需要把上一次的调用回调函数返回值传递给下一个then作为参数,那么如何传递就需要在返回的promise对象中执行resolve或者reject方法更改状态,从而使用**then方法指定对应状态的回调函数**。

    class testPromise {  .... // 上面代码暂省略  
    
      then (successCallback, failCallback) {
        /**
         * 判断状态,并根据状态指定相应的回调函数
         * 那么成功后的值,失败后的值在哪呢,就是resolve和reject方法在调用后传递的值
         */
        let promise2 = new testPromise ((resolve, reject) => {      if (this.status === FULFILLED) {
            // 2.保存上一次then方法回调函数的返回值
            let x = successCallback(this.value);
            // 3.将上一次回到函数的返回值作为参数,传递给下一个then方法调用
            resolve(x);
          } else if (this.status === REJECTED) {
            let y = failCallback(this.reason);
            reject(y);
          } else {
            return;
          }
        })
        // 1.返回promsie对象
        return promise2
    
      }
    }
    

7.加入异步逻辑

index.js

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

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

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

testPromise.js

// 定义异步中【等待】状态的存储成功回调函数
successValue = undefined;
// 定义异步中【等待】状态的存储失败回调函数
failValue = undefined;

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

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

    /**
     * 此时的successValue存放的是successCallback这个回调函数,
     * 在调用2s后,执行resolve方法后,状态变为fulfilled,
     * 那么调用then方法,即调用成功后的回调函数,此时this.successValue就是这个回调函数
     */
    if (this.successValue) this.successValue(this.value);
  }

// then方法中
/**
   * 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) {
          // 2.保存上一次then方法回调函数的返回值
          let x = successCallback(this.value);
          // 3.将上一次回到函数的返回值作为参数,传递给下一个then方法调用
          resolve(x);
      } else if (this.status === REJECTED) {
          let y = failCallback(this.reason);
          reject(y);
        
      } 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

  }

8.重复调用then方法,非链式调用,如下

index.js

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

碰到的问题

上面调用的话,如果是同步执行,那么then方法内可以那到明确的成功还是失败状态就可以判断需要调用哪个回调函数,如果是异步执行,那在异步逻辑中,我们现在的处理只可以存放一组数据,实际调用有三次,那么就有三组异步执行,当前逻辑不符合调用。

解决思路

所以需要在异步处理中,加入对这部分的处理,

  1. 把每次调用的异步回调函数存存储,并在resolve方法中依次执行。
  2. class类中属性successValue``(failReason)改为数组。
  3. then方法异步处理中,将每一个then的回调函数push进数组。
  4. resolve方法中,依次执行存储数组中的每个回调,并倒叙排序,让第一个进数组的先执行。

直接上代码:

// 定义状态
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方法在调用后传递的值
     */
    let promise2 = new testPromise ((resolve, reject) => {
      if (this.status === FULFILLED) {
        let x = successCallback(this.value);
        resolve(x);
      } else if (this.status === REJECTED) {
        let y = failCallback(this.reason);
        reject(y);
      } 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;
      }
    })
    
    return promise2

  }
}


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

testPromise.js

// 定义状态
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方法在调用后传递的值
     */
    let promise2 = new testPromise ((resolve, reject) => {
      if (this.status === FULFILLED) {
        let x = successCallback(this.value);
        resolve(x);
      } else if (this.status === REJECTED) {
        let y = failCallback(this.reason);
        reject(y);
      } 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;
      }
    })
    
    return promise2

  }
}


module.exports = testPromise;

至此,以上8点promise基本功能自定义已经实现。

注:转发、转载、复制等,请附带本文链接:

https://juejin.cn/post/6924228106815275015/