day3_手写promise

62 阅读12分钟

1.搭建架子

(function (window) {
  // 定义常量
  const PENDING = "pending";
  const FULFILLED = "fulfilled";
  const REJECTED = "rejected";

  class MyPromise {
    constructor(executor) {
      // 定义this,防止this指向发生错误
      const _this = this;

      function resolve(value) {}

      function reject(reason) {}
      
      executor(resolve, reject);
    }
    // 返回一个promise
    then() {}
    // 返回一个promise
    catch() {}
    static resolve() {}
    static reject() {}
    static all() {}
    static race() {}
  }

  console.dir(MyPromise);

  window.MyPromise = MyPromise;
})(window);


2.resolve和reject处理状态

  • 我们都知道,Promise有三种状态
    • pending
    • fulfilled
    • rejected
  • 且状态只能改变一次,且不可逆,即我们即使在Promise内部多次调用resolve/reject,只会触发第一次
    • pending -> fulfilled
      • 当我们执行resolve时触发
    • pending -> rejected
      • 当我们执行reject时触发
  • 由此可以得出,Promise内部执行这两个方法的时候,会改变Promise的状态,且将我们调用的参数传递进去保存

image.png

image.png

image.png

(function (window) {
  // 定义常量
  const PENDING = "pending";
  const FULFILLED = "fulfilled";
  const REJECTED = "rejected";

  class MyPromise {
    constructor(executor) {
      // 定义this,防止this指向发生错误
      const _this = this;
      // 初始化参数
      _this.PromiseState = PENDING;
      _this.PromiseResult = null;

      // 主要用于更改Promise的状态和结果
      function resolve(value) {
        // 当状态不是pending时,不处理
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = FULFILLED;
        _this.PromiseResult = value;
      }

      function reject(reason) {
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = REJECTED;
        _this.PromiseResult = reason;
      }

      executor(resolve, reject);
    }
    // 返回一个promise
    then() {}
    // 返回一个promise
    catch() {}
    static resolve() {}
    static reject() {}
    static all() {}
    static race() {}
  }

  console.dir(MyPromise);

  window.MyPromise = MyPromise;
})(window);

3.then的调用

  • 我们都知道,当调用then的时候,会根据当前Promise的状态,来执行then的第一个回调还是第二个回调
    • 第一个回调:PromiseState == 'fulfilled'时调用
    • 第二个回调:PromiseState == 'rejected'时调用
(function (window) {
  // 定义常量
  const PENDING = "pending";
  const FULFILLED = "fulfilled";
  const REJECTED = "rejected";

  class MyPromise {
    constructor(executor) {
      // 定义this,防止this指向发生错误
      const _this = this;
      // 初始化参数
      _this.PromiseState = PENDING;
      _this.PromiseResult = null;
      // 模拟微队列,用于存放回调
      _this.callbacks = [];

      // 主要用于更改Promise的状态和结果
      function resolve(value) {
        // 当状态不是pending时,不处理
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = FULFILLED;
        _this.PromiseResult = value;
      }

      function reject(reason) {
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = REJECTED;
        _this.PromiseResult = reason;
      }

      executor(resolve, reject);
    }
    // 返回一个promise
    then(onResolve, onReject) {
      // 因为then是同步任务,当Promise内部为异步任务时,会导致then获取不到当前的状态和值,
      // 所以当状态为pending时,我们应该将回调存进队列内,当Promise内部执行完毕后,触发回调
      if (this.PromiseState === PENDING) {
        this.callbacks.push({
          onResolve,
          onReject,
        });
      }
    }

  window.MyPromise = MyPromise;
})(window);

当Promise内部执行完毕后,会根据执行的结果,触发then的回调

(function (window) {
  // 定义常量
  const PENDING = "pending";
  const FULFILLED = "fulfilled";
  const REJECTED = "rejected";

  class MyPromise {
    constructor(executor) {
      // 定义this,防止this指向发生错误
      const _this = this;
      // 初始化参数
      _this.PromiseState = PENDING;
      _this.PromiseResult = null;
      // 模拟微队列,用于存放回调
      _this.callbacks = [];

      // 主要用于更改Promise的状态和结果
      function resolve(value) {
        // 当状态不是pending时,不处理
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = FULFILLED;
        _this.PromiseResult = value;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onResolve(_this.PromiseResult);
            });
          });
        }
      }

      function reject(reason) {
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = REJECTED;
        _this.PromiseResult = reason;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onReject(_this.PromiseResult);
            });
          });
        }
      }

      executor(resolve, reject);
    }
    // 返回一个promise
    then(onResolve, onReject) {
      // 因为then是同步任务,当Promise内部为异步任务时,会导致then获取不到当前的状态和值,
      // 所以当状态为pending时,我们应该将回调存进队列内,当Promise内部执行完毕后,触发回调
      if (this.PromiseState === PENDING) {
        this.callbacks.push({
          onResolve,
          onReject,
        });
      }
    }
  }

  console.dir(MyPromise);

  window.MyPromise = MyPromise;
})(window);

4.实现then的链式调用

(function (window) {
  // 定义常量
  const PENDING = "pending";
  const FULFILLED = "fulfilled";
  const REJECTED = "rejected";

  class MyPromise {
    constructor(executor) {
      // 定义this,防止this指向发生错误
      const _this = this;
      // 初始化参数
      _this.PromiseState = PENDING;
      _this.PromiseResult = null;
      // 模拟微队列,用于存放回调
      _this.callbacks = [];

      // 主要用于更改Promise的状态和结果
      function resolve(value) {
        // 当状态不是pending时,不处理
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = FULFILLED;
        _this.PromiseResult = value;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onResolve(_this.PromiseResult);
            });
          });
        }
      }

      function reject(reason) {
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = REJECTED;
        _this.PromiseResult = reason;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onReject(_this.PromiseResult);
            });
          });
        }
      }

      executor(resolve, reject);
    }
    // 返回一个promise
    then(onResolve, onReject) {
      const _this = this;
      return new MyPromise((resolve, reject) => {
        // 因为then是同步任务,当Promise内部为异步任务时,会导致then获取不到当前的状态和值,
        // 所以当状态为pending时,我们应该将回调存进队列内,当Promise内部执行完毕后,触发回调
        if (_this.PromiseState === PENDING) {
          this.callbacks.push({
            onResolve,
            onReject,
          });
        } else if (_this.PromiseState === FULFILLED) {
          // 当状态为成功时,调用then方法
          const result = onResolve(_this.PromiseResult);
          // 此时存在两种情况,
          // 1.result为非Promise,则result为Promise的值
          // 2.result为Promise
          // 2.1根据Promise的结果,来决定当前Promise的最新状态和值
          if (result instanceof MyPromise) {
            // result.then(
            //   (value) => {
            //     resolve(value);
            //   },
            //   (reason) => {
            //     reject(reason);
            //   }
            // );
            // 简写
            result.then(resolve, reject);
          } else {
            resolve(result);
          }
        } else {
        }
      });
    }
    // 返回一个promise
    catch() {}
    static resolve() {}
    static reject() {}
    static all() {}
    static race() {}
  }

  console.dir(MyPromise);

  window.MyPromise = MyPromise;
})(window);

同理,当PromiseState == rejected的时候,逻辑和fulfilled是一样的,所以我们可以抽离进行封装, 且then内部为微任务,这里用定时器模拟

(function (window) {
  // 定义常量
  const PENDING = "pending";
  const FULFILLED = "fulfilled";
  const REJECTED = "rejected";

  class MyPromise {
    constructor(executor) {
      // 定义this,防止this指向发生错误
      const _this = this;
      // 初始化参数
      _this.PromiseState = PENDING;
      _this.PromiseResult = null;
      // 模拟微队列,用于存放回调
      _this.callbacks = [];

      // 主要用于更改Promise的状态和结果
      function resolve(value) {
        // 当状态不是pending时,不处理
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = FULFILLED;
        _this.PromiseResult = value;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onResolve(_this.PromiseResult);
            });
          });
        }
      }

      function reject(reason) {
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = REJECTED;
        _this.PromiseResult = reason;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onReject(_this.PromiseResult);
            });
          });
        }
      }
      // 我们都知道,Promise内部如果抛出异常,则相当于调用了reject
      // 这里用trycatch进行捕获
      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }
    // 返回一个promise
    then(onResolve, onReject) {
      return new MyPromise((resolve, reject) => {
        function handler(callback) {
          // 我们都知道,Promise内部如果抛出异常,则相当于调用了reject
          // 这里用trycatch进行捕获
          try {
            // 当状态为成功时,调用then方法
            const result = callback(_this.PromiseResult);
            // 此时存在两种情况,
            // 1.result为非Promise,则result为Promise的值
            // 2.result为Promise
            // 2.1根据Promise的结果,来决定当前Promise的最新状态和值
            if (result instanceof MyPromise) {
              // 简写
              result.then(resolve, reject);
            } else {
              resolve(result);
            }
          } catch (error) {
            reject(error);
          }
        }
        // 因为then是同步任务,当Promise内部为异步任务时,会导致then获取不到当前的状态和值,
        // 所以当状态为pending时,我们应该将回调存进队列内,当Promise内部执行完毕后,触发回调
        if (_this.PromiseState === PENDING) {
          this.callbacks.push({
            onResolve,
            onReject,
          });
        } else if (_this.PromiseState === FULFILLED) {
          // 因为 then内部为微任务,所以用定时器模拟
          setTimeout(() => {
            handler(onResolve);
          });
        } else {
          setTimeout(() => {
            handler(onReject);
          });
        }
      });
    }
  }

  console.dir(MyPromise);

  window.MyPromise = MyPromise;
})(window);

此时,我们已经处理好then成功和失败的回调,但当状态为pending时,我们调用then方法的回调,并没有对状态进行变更,所以我们需要对该状态进行修改

(function (window) {
  // 定义常量
  const PENDING = "pending";
  const FULFILLED = "fulfilled";
  const REJECTED = "rejected";

  class MyPromise {
    constructor(executor) {
      // 定义this,防止this指向发生错误
      const _this = this;
      // 初始化参数
      _this.PromiseState = PENDING;
      _this.PromiseResult = null;
      // 模拟微队列,用于存放回调
      _this.callbacks = [];

      // 主要用于更改Promise的状态和结果
      function resolve(value) {
        // 当状态不是pending时,不处理
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = FULFILLED;
        _this.PromiseResult = value;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onResolve(_this.PromiseResult);
            });
          });
        }
      }

      function reject(reason) {
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = REJECTED;
        _this.PromiseResult = reason;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onReject(_this.PromiseResult);
            });
          });
        }
      }
      // 我们都知道,Promise内部如果抛出异常,则相当于调用了reject
      // 这里用trycatch进行捕获
      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }
    // 返回一个promise
    then(onResolve, onReject) {
      const _this = this;
      return new MyPromise((resolve, reject) => {
        function handler(callback) {
          // 我们都知道,Promise内部如果抛出异常,则相当于调用了reject
          // 这里用trycatch进行捕获
          try {
            // 当状态为成功时,调用then方法
            const result = callback(_this.PromiseResult);
            // 此时存在两种情况,
            // 1.result为非Promise,则result为Promise的值
            // 2.result为Promise
            // 2.1根据Promise的结果,来决定当前Promise的最新状态和值
            if (result instanceof MyPromise) {
              // 简写
              result.then(resolve, reject);
            } else {
              resolve(result);
            }
          } catch (error) {
            reject(error);
          }
        }
        // 因为then是同步任务,当Promise内部为异步任务时,会导致then获取不到当前的状态和值,
        // 所以当状态为pending时,我们应该将回调存进队列内,当Promise内部执行完毕后,触发回调
        if (_this.PromiseState === PENDING) {
          // pending状态和fulfilled状态一样,也需要判断结果是否为Promise
          this.callbacks.push({
            onResolve() {
              handler(onResolve);
            },
            onReject() {
              handler(onReject);
            },
          });
        } else if (_this.PromiseState === FULFILLED) {
          // 因为 then内部为微任务,所以用定时器模拟
          setTimeout(() => {
            handler(onResolve);
          });
        } else {
          setTimeout(() => {
            handler(onReject);
          });
        }
      });
    }
  }

  console.dir(MyPromise);

  window.MyPromise = MyPromise;
})(window);

5.catch的完善

当我们完成then之后,catch其实原理和then一样,所以我们可以利用then来实现

    // 返回一个promise
    catch(onReject) {
      return this.then(undefined, onReject);
    }

6.Promise对象的resolve和reject的实现

    // 调用resolve时,会返回一个Promise对象
    // 2.1 当resolve的参数为promise时
    // 2.2 当resolve的参数不为promise时,其结果就是promise的结果
    static resolve(value) {
      return new MyPromise((resolve, reject) => {
        if (value instanceof MyPromise) {
          value.then(resolve, reject);
        } else {
          resolve(value);
        }
      });
    }
    // 调用reject时,会返回一个Promise对象
    // 与resolve不同的是,reject传入的参数是什么,Promise的结果就是什么
    static reject(reason) {
      return new MyPromise((resovle, reject) => {
        reject(reason);
      });
    }

7.优化then

    // 返回一个promise
   then(onResolve, onReject) {
     const _this = this;
     // 优化,当onResolve,onReject不是函数的时候
     // 实现catch和then的参数穿透
     onResolve =
       typeof onResolve === "function" ? onResolve : (value) => value;
     onReject =
       typeof onReject === "function"
         ? onReject
         : (reason) => {
             throw reason;
           };
     return new MyPromise((resolve, reject) => {
       function handler(callback) {
         // 我们都知道,Promise内部如果抛出异常,则相当于调用了reject
         // 这里用trycatch进行捕获
         try {
           // 当状态为成功时,调用then方法
           const result = callback(_this.PromiseResult);

           // 此时存在两种情况,
           // 1.result为非Promise,则result为Promise的值
           // 2.result为Promise
           // 2.1根据Promise的结果,来决定当前Promise的最新状态和值
           if (result instanceof MyPromise) {
             // 简写
             result.then(resolve, reject);
           } else {
             resolve(result);
           }
         } catch (error) {
           reject(error);
         }
       }
       // 因为then是同步任务,当Promise内部为异步任务时,会导致then获取不到当前的状态和值,
       // 所以当状态为pending时,我们应该将回调存进队列内,当Promise内部执行完毕后,触发回调
       if (_this.PromiseState === PENDING) {
         // pending状态和fulfilled状态一样,也需要判断结果是否为Promise
         _this.callbacks.push({
           onResolve() {
             handler(onResolve);
           },
           onReject() {
             handler(onReject);
           },
         });
       } else if (_this.PromiseState === FULFILLED) {
         // 因为 then内部为微任务,所以用定时器模拟
         setTimeout(() => {
           handler(onResolve);
         });
       } else {
         setTimeout(() => {
           handler(onReject);
         });
       }
     });
   }

image.png

8.all和race的实现

    // 当成功时,返回一个数组
   // 当有一个失败时,返回失败的结果
   static all(promises) {
     return new MyPromise((resolve, reject) => {
       // 返回值
       const values = new Array(promises.length);
       // 成功的累加
       let resolveCount = 0;
       promises.forEach((p, index) => {
         // 包一层,防止传入的参数不为promise
         MyPromise.resolve(p).then(
           (value) => {
             resolveCount++;
             values[index] = value;
             if (resolveCount === promises.length) {
               resolve(values);
             }
           },
           (reason) => {
             reject(reason);
           }
         );
       });
     });
   }
   // 当有一个先成功时,就返回该值
   static race(promises) {
     return new MyPromise((resolve, reject) => {
       promises.forEach((p) => {
         p.then(
           (value) => {
             resolve(value);
           },
           (reason) => {
             reject(reason);
           }
         );
       });
     });
   }

image.png

image.png

image.png

9.完整代码

(function (window) {
  // 定义常量
  const PENDING = "pending";
  const FULFILLED = "fulfilled";
  const REJECTED = "rejected";

  class MyPromise {
    constructor(executor) {
      // 定义this,防止this指向发生错误
      const _this = this;
      // 初始化参数
      _this.PromiseState = PENDING;
      _this.PromiseResult = null;
      // 模拟微队列,用于存放回调
      _this.callbacks = [];

      // 主要用于更改Promise的状态和结果
      function resolve(value) {
        // 当状态不是pending时,不处理
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = FULFILLED;
        _this.PromiseResult = value;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onResolve(_this.PromiseResult);
            });
          });
        }
      }

      function reject(reason) {
        if (_this.PromiseState !== PENDING) return;

        _this.PromiseState = REJECTED;
        _this.PromiseResult = reason;

        if (_this.callbacks.length) {
          // 因为then内部需要晚于Promise的外部的同步任务,即微任务执行,这里采用定时器模拟
          setTimeout(() => {
            _this.callbacks.forEach((callbackObj) => {
              callbackObj.onReject(_this.PromiseResult);
            });
          });
        }
      }
      // 我们都知道,Promise内部如果抛出异常,则相当于调用了reject
      // 这里用trycatch进行捕获
      try {
        executor(resolve, reject);
      } catch (error) {
        reject(error);
      }
    }
    // 返回一个promise
    then(onResolve, onReject) {
      const _this = this;
      // 优化,当onResolve,onReject不是函数的时候
      onResolve =
        typeof onResolve === "function" ? onResolve : (value) => value;
      onReject =
        typeof onReject === "function"
          ? onReject
          : (reason) => {
              throw reason;
            };
      return new MyPromise((resolve, reject) => {
        function handler(callback) {
          // 我们都知道,Promise内部如果抛出异常,则相当于调用了reject
          // 这里用trycatch进行捕获
          try {
            // 当状态为成功时,调用then方法
            const result = callback(_this.PromiseResult);

            // 此时存在两种情况,
            // 1.result为非Promise,则result为Promise的值
            // 2.result为Promise
            // 2.1根据Promise的结果,来决定当前Promise的最新状态和值
            if (result instanceof MyPromise) {
              // 简写
              result.then(resolve, reject);
            } else {
              resolve(result);
            }
          } catch (error) {
            reject(error);
          }
        }
        // 因为then是同步任务,当Promise内部为异步任务时,会导致then获取不到当前的状态和值,
        // 所以当状态为pending时,我们应该将回调存进队列内,当Promise内部执行完毕后,触发回调
        if (_this.PromiseState === PENDING) {
          // pending状态和fulfilled状态一样,也需要判断结果是否为Promise
          _this.callbacks.push({
            onResolve() {
              handler(onResolve);
            },
            onReject() {
              handler(onReject);
            },
          });
        } else if (_this.PromiseState === FULFILLED) {
          // 因为 then内部为微任务,所以用定时器模拟
          setTimeout(() => {
            handler(onResolve);
          });
        } else {
          setTimeout(() => {
            handler(onReject);
          });
        }
      });
    }
    // 返回一个promise
    catch(onReject) {
      return this.then(undefined, onReject);
    }
    // 调用resolve时,会返回一个Promise对象
    // 2.1 当resolve的参数为promise时
    // 2.2 当resolve的参数不为promise时,其结果就是promise的结果
    static resolve(value) {
      return new MyPromise((resolve, reject) => {
        if (value instanceof MyPromise) {
          value.then(resolve, reject);
        } else {
          resolve(value);
        }
      });
    }
    // 调用reject时,会返回一个Promise对象
    // 与resolve不同的是,reject传入的参数是什么,Promise的结果就是什么
    static reject(reason) {
      return new MyPromise((resovle, reject) => {
        reject(reason);
      });
    }
    // 当成功时,返回一个数组
    // 当有一个失败时,返回失败的结果
    static all(promises) {
      return new MyPromise((resolve, reject) => {
        // 返回值
        const values = new Array(promises.length);
        // 成功的累加
        let resolveCount = 0;
        promises.forEach((p, index) => {
          // 包一层,防止传入的参数不为promise
          MyPromise.resolve(p).then(
            (value) => {
              resolveCount++;
              values[index] = value;
              if (resolveCount === promises.length) {
                resolve(values);
              }
            },
            (reason) => {
              reject(reason);
            }
          );
        });
      });
    }
    // 当有一个先成功时,就返回该值
    static race(promises) {
      return new MyPromise((resolve, reject) => {
        promises.forEach((p) => {
          p.then(
            (value) => {
              resolve(value);
            },
            (reason) => {
              reject(reason);
            }
          );
        });
      });
    }
  }

  window.MyPromise = MyPromise;
})(window);

10.总结

  • promise有三种状态
    • pending
    • fulfilled
    • rejected
  • promise的状态变更,只能从
    • pending -> fulfilled
    • pending -> rejected
  • promise内部为同步任务,当.then内部为微任务
  • promise内部
    • 若没有resolve或者reject调用,则相当于调用resolve(undefined)
    • 若调用resolve,则then触发第一个回调,
    • 若调用reject,则then触发第二个回调
      • 若没有写then第二个参数,则会一直传递到catch上
      • 即若存在多个then,且then都没有第二个参数,则reject会一直传递到外部的catch上
    • 若resolve返回的是一个Promise
      • 则then的状态由Promise的结果来决定
    • 若promise内部抛出异常,则相当于调用了reject