最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • JavaScript从零到一实现Promise的亿点点细节

    正文概述 掘金(前端关宇)   2021-03-21   581

    JavaScript从零到一实现Promise的亿点点细节

    1 实现自己定义的 Promise 类 并且绑定 this

    class Yu {
      static PENDING = "pending";
      static FUIFILLED = "fulfilled";
      static REJECTED = "rejected";
      constructor(executor) {
        this.state = YU.PENDING;
        this.value = null;
        executor(this.resolve, this.reject);
      }
      resolve(value) {}
      reject(reason) {}
    }
    
    let p = new Yu((resolve, reject) => {
      resolve(1);
    });
    console.log(p);
    

    不绑定 this 的时候 会报错, 因为 es6 class 默认开启严格模式,想要不报错就使用 bind 绑定 this 即可

    executor(this.resolve.bind(this), this.reject.bind(this));
    

    但是 resolve reject 同步同时调用的情况会后面的把前面的状态覆盖,我们期望的是状态从 pending 变成 rejected 或者 pending 变成 fulfilled 以后就不要再变了。 下面通过状态保护来解决

    2 状态保护以及 executor 的异常捕获的处理

    状态保护很简单,就是 resolve reject 里面加判断即可 判断当前状态是 pending 再做里面的处理

      resolve(value) {
        if (this.state === Yu.PENDING) {
          this.state = Yu.FUIFILLED;
          this.value = value;
        }
      }
      reject(reason) {
        if (this.state === Yu.PENDING) {
          this.state = Yu.REJECTED;
          this.value = reason;
        }
      }
    

    当我们这样使用的时候 ,有可能有这样的情况

    new Yu((resolve, reject) => {
      //使用一个错误的变量
      console.log(bbb);
    
      //或者主动抛出一个错误
      throw 2222;
      //
    });
    

    我们都需要对 executor 进行错误处理, 内部使用 try catch 处理,再内部修改 catch 里面的状态

    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (e) {
      this.reject(e);
    }
    

    这样就可以了

    3 then 的基础 pending fulfilled rejected 的处理

    JavaScript从零到一实现Promise的亿点点细节

    JavaScript从零到一实现Promise的亿点点细节

    • Promises/A+ 规范写明,必须提供一个then方法, 该 方法接收两个参数,onFulfilled 和onRejected, then(onFulfilled, onRejected) ,第一个参数是成功的回调,第二个参数是失败的回调
    • 两个参数都是可选的 , 意思.then() 的时候要做then值穿透的处理,其实就是返回当前Promise实例的里面的value
    • 当onFulfilled是函数的时候, 必须且只能在当前状态是fulfilled的情况才可以调用,之前不行,也不可以调用多次,
    • 当onFulfilled是函数的时候,必须且只能在当前状态是rejected的情况才可以调用,之前不行,也不可以调用多次,
    • then可以在同一个promise实例上调用多次
    • then 必须返回新的Promise实例 promise2 = promise1.then(onFulfilled, onRejected)

    then 是原型上的方法 接收两个回调函数作为参数。

    
    then(onFulfilled, onRejected) {
      //先让传进来的第一个函数执行 ,并且将实例上的value传递给当前函数
      onFulfilled(this.value);
    }
    

    调用下看一下效果,当调用 resolve('success') 内部将 succss 保存在实例的 value 属性上并且改变当前的状态为成功状态(fulfilled), 并且在调用 then 的时候,执行传递进来的 onFulfilled 方法的时候,将 this.value 也就是 success 传递给 onFulfilled,这样就可以再调用的时候 ,输出形参 value,也就是实参,success 了。

    let p = new Yu((resolve, reject) => {
      resolve("success");
    }).then(val => {
      console.log(val); //success
    });
    

    但是当我们注释掉 resolve 的时候 ,不应该有输出, 但是现在还是有输出,所有我们不能上来就在 then 的里面执行这个成功的方法,而是应该在一定的情况下执行,什么情况呢? 就是当前状态为 fulfilled 的请求,同理, 其他两种情况也类似, 代码修改为:

    then(onFulfilled, onRejected) {
      //1  两个参数的校验 todo
      //2  执行两个传进来的函数 什么时候执行 什么时候不执行?
      this.state === Yu.FUIFILLED && onFulfilled(this.value);
      this.state === Yu.REJECTED && onRejected(this.value);
      if (this.state === Yu.PENDING) {
        //因为不知道什么时候成功或者失败,所以要暂时将函数保存
      }
      //3 返回一个新的Promise实例  为什么需要返回新的实例? 因为可以支持链式调用then 就是递归 函数每次调用自己  那我可以直接返回this 下次使用then的时候通过查找原型链上的then方法
      //也可以实现链式调用呀? 那为什么不用直接返回this的方法 而是 使用重新new 一个构造函数 得到一个新的实例的方法呢? 因为需要传递上次的状态
      //而且返回的不能和上一个实例相同 如果相同应该给一个报错的提示
    }
    

    调用的时候 ,注意 then 方法接受两个回调函数作为参数 第一个是成功的函数 第二个是失败的函数。 里面的参数是当前实例上的 value, 如果没传的第一个或者第二个参数的时候,不要报错,这时候就要我们在 then 里面做参数的处理

    then(onFulfilled, onRejected) {
      //1  两个参数的校验
      onFulfilled =
        typeof onFulfilled === "function" ? onFulfilled : () => {};
      onRejected = typeof onRejected === "function" ? onRejected : () => {};
      //2  执行两个传进来的函数 是对应的状态的时候 才执行
      this.state === Yu.FUIFILLED && onFulfilled(this.value);
      this.state === Yu.REJECTED && onRejected(this.value);
      if (this.state === Yu.PENDING) {
        //因为不知道什么时候成功或者失败,所以要暂时将函数保存
      }
    
    }
    

    错误处理的时候, 也要把异常捕获。 所以还要完善一下错误处理的函数 里面自己抛出异常

    //1  两个参数的校验
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : () => {};
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : err => {
            throw err;
          };
    

    调用的结果可以是这样

    let p = new Yu((resolve, reject) => {
      resolve("success");
    }).then(
      val => {
        console.log(val);
      }
      //不传第二个参数 也不会报错, 因为then内部做了处理
      // reason => {
      //   console.log(reason);
      // }
    );
    

    也可以是这样调用

    let p = new Yu((resolve, reject) => {
      reject("失败");
    }).then(
      //第一个参数不是函数 也不会报错, 因为then内部做了处理
      null,
      reason => {
        console.log(reason);
      }
    );
    

    还有个问题是 如果 then 里面 onFulfilled 和 onRejected 里面也有错误,也需要在内部做 try catch 的处理

    //2  执行两个传进来的函数 是对应的状态的时候 才执行
    if (this.state === Yu.FUIFILLED) {
      try {
        onFulfilled(this.value);
      } catch (e) {
        onRejected(this.value);
      }
    }
    
    if (this.state === Yu.REJECTED) {
      try {
        onRejected(this.value);
      } catch (e) {
        onRejected(this.value);
      }
    }
    

    另一个细节是, new Promise 里面的 resovle 是同步执行还是异步执行的? 我们可以看一下,正常的 promsie 的输出顺序

    let p2 = new Promise((resolve, reject) => {
      console.log(1111);
      resolve("成功");
      console.log(2222);
    }).then(val => console.log(val));
    

    所以我们要把 resolve reject 变成异步的 ,可以用 setTimeout 模拟一下

    const fakeMicroTask = (cb, timer = 1000) => setTimeout(cb, timer);
    
    //2  执行两个传进来的函数 是对应的状态的时候 才执行
    if (this.state === Yu.FUIFILLED) {
      fakeMicroTask(() => {
        try {
          onFulfilled(this.value);
        } catch (e) {
          onRejected(this.value);
        }
      });
    }
    
    if (this.state === Yu.REJECTED) {
      fakeMicroTask(() => {
        try {
          onRejected(this.value);
        } catch (e) {
          onRejected(this.value);
        }
      });
    }
    

    如果 new Promise 里面 异步调用 resolve 或者 reject 的时候, 我们看看正常的 promise 的执行顺序:

    let p2 = new Promise((resolve, reject) => {
      console.log(1111);
      setTimeout(() => {
        resolve("成功");
      }, 1000);
      console.log(2222);
    }).then(val => console.log(val));
    console.log("first");
    //输出顺序
    //1111
    //2222
    //first
    //一秒后 输出 成功
    

    所以我们要对异步调用 resolve 的情况做处理, 这时候就是对 pending 状态做处理 ,因为一开始状态就是 pending ,要等一秒后才会改变状态

    const fakeMicroTask = (cb, timer = 1000) => setTimeout(cb, timer);
    class Yu {
      static PENDING = "pending";
      static FUIFILLED = "fulfilled";
      static REJECTED = "rejected";
      constructor(executor) {
        this.state = Yu.PENDING;
        this.value = null;
        this.callbacks = [];
    
        try {
          executor(this.resolve.bind(this), this.reject.bind(this));
        } catch (e) {
          this.reject(e);
        }
      }
      resolve(value) {
        if (this.state === Yu.PENDING) {
          this.state = Yu.FUIFILLED;
          this.value = value;
    
          this.callbacks.forEach(cbObj => {
            cbObj.onFulfilled(value);
          });
        }
      }
      reject(reason) {
        if (this.state === Yu.PENDING) {
          this.state = Yu.REJECTED;
          this.value = reason;
    
          this.callbacks.forEach(cbObj => {
            cbObj.onRejected(reason);
          });
        }
      }
    
      then(onFulfilled, onRejected) {
        //1  两个参数的校验
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : () => {};
        onRejected =
          typeof onRejected === "function"
            ? onRejected
            : err => {
                throw err;
              };
        //2  执行两个传进来的函数 是对应的状态的时候 才执行
        if (this.state === Yu.FUIFILLED) {
          fakeMicroTask(() => {
            try {
              onFulfilled(this.value);
            } catch (e) {
              onRejected(this.value);
            }
          });
        }
    
        if (this.state === Yu.REJECTED) {
          fakeMicroTask(() => {
            try {
              onRejected(this.value);
            } catch (e) {
              onRejected(this.value);
            }
          });
        }
        if (this.state === Yu.PENDING) {
          //因为不知道什么时候成功或者失败,所以要暂时将函数保存
          this.callbacks.push({
            onFulfilled,
            onRejected
          });
        }
      }
    }
    

    pending 状态异常处理

    //没有加错误处理的情况
    if (this.state === Yu.PENDING) {
      //因为不知道什么时候成功或者失败,所以要暂时将函数保存
      this.callbacks.push({
        onFulfilled,
        onRejected
      });
    }
    
    //对pending 状态加了错误处理的情况
    if (this.state === Yu.PENDING) {
      //因为不知道什么时候成功或者失败,所以要暂时将函数保存
      this.callbacks.push({
        onFulfilled: value => {
          try {
            onFulfilled(value);
          } catch (err) {
            onRejected(err);
          }
        },
        onRejected: reason => {
          try {
            onRejected(value);
          } catch (err) {
            onRejected(err);
          }
        }
      });
    }
    

    下面的输出顺序是? :

    let p = new Yu((resolve, reject) => {
      setTimeout(() => {
        resolve("success");
        console.log(111);
      });
    }).then(value => console.log(value), reason => console.log(reason));
    console.log("first");
    

    1 first 2 success 3 111

    但是 正常 promise 输出的顺序是

    1 first 2 111 3 success

    所以我们要做一下改造: 改成异步即可

    resolve(value) {
      if (this.state === Yu.PENDING) {
        this.state = Yu.FUIFILLED;
        this.value = value;
    
        fakeMicroTask(() => {
          this.callbacks.forEach(cbObj => {
            cbObj.onFulfilled(value);
          });
        });
      }
    }
    reject(reason) {
      if (this.state === Yu.PENDING) {
        this.state = Yu.REJECTED;
        this.value = reason;
    
        fakeMicroTask(() => {
          this.callbacks.forEach(cbObj => {
            cbObj.onRejected(reason);
          });
        });
      }
    }
    

    4 then 支持链式调用

    先调用 一下

    let p = new Yu((resolve, reject) => {
      setTimeout(() => {
        resolve("success");
        console.log(111);
      });
      console.log(2222);
    })
      .then(value => console.log(value), reason => console.log(reason))
      .then(val => console.log(val));
    

    Uncaught TypeError: Cannot read property 'then' of undefined

    • 链式调用 所以要返回一个新的 promise

    • new promise 里面 reject 不会影响 then 里面的状态?

    实现链式调用:

    then(onFulfilled, onRejected) {
    //1  两个参数的校验
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : () => {};
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : err => {
            throw err;
          };
    //3 返回一个新的Promise实例
    let newThen = new Yu((resolve, reject) => {
      //2  执行两个传进来的函数 是对应的状态的时候 才执行
      if (this.state === Yu.FUIFILLED) {
        fakeMicroTask(() => {
          try {
            onFulfilled(this.value);
          } catch (e) {
            onRejected(this.value);
          }
        });
      }
    
      if (this.state === Yu.REJECTED) {
        fakeMicroTask(() => {
          try {
            onRejected(this.value);
          } catch (e) {
            onRejected(this.value);
          }
        });
      }
      if (this.state === Yu.PENDING) {
        //因为不知道什么时候成功或者失败,所以要暂时将函数保存
        this.callbacks.push({
          onFulfilled: value => {
            try {
              onFulfilled(value);
            } catch (err) {
              onRejected(err);
            }
          },
          onRejected: reason => {
            try {
              onRejected(value);
            } catch (err) {
              onRejected(err);
            }
          }
        });
      }
    });
    return newThen;
    }
    

    then 值穿透的问题 的处理

    参数校验的时候 直接返回this.value 就行
    
     //1  两个参数的校验
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : () => this.value;
    onRejected =
      typeof onRejected === "function" ? onRejected : () => this.value;
    

    then 新增promise 异常处理

    then(onFulfilled, onRejected) {
              //1  两个参数的校验
              onFulfilled =
                typeof onFulfilled === "function" ? onFulfilled : () => this.value;
              onRejected =
                typeof onRejected === "function" ? onRejected : () => this.value;
              // onRejected =
              //   typeof onRejected === "function"
              //     ? onRejected
              //     : err => {
              //         throw err;
              //       };
              //3.1 新建一个新的实例
              let newPromiseInstance = new Yu((resolve, reject) => {
                //2  执行两个传进来的函数 是对应的状态的时候 才执行
                if (this.state === Yu.FUIFILLED) {
                  fakeMicroTask(() => {
                    try {
                      let result = onFulfilled(this.value);
                      resolve(result);
                    } catch (e) {
                      reject(e);
                    }
                  });
                }
    
                if (this.state === Yu.REJECTED) {
                  fakeMicroTask(() => {
                    try {
                      let result = onRejected(this.value);
                      //这里也是要resolve 只是处理上次then onRejected 里面返回的值
                      resolve(result);
                    } catch (e) {
                      reject(e);
                    }
                  });
                }
                if (this.state === Yu.PENDING) {
                  //因为不知道什么时候成功或者失败,所以要暂时将函数保存
                  this.callbacks.push({
                    onFulfilled: value => {
                      try {
                        let result = onFulfilled(value);
                        resolve(result);
                      } catch (err) {
                        reject(err);
                      }
                    },
                    onRejected: reason => {
                      try {
                        let result = onRejected(reason);
                        resolve(result);
                      } catch (err) {
                        reject(err);
                      }
                    }
                  });
                }
              });
              //3.2 返回一个新的promise实例
              return newPromiseInstance;
            }
    

    调用看一下处理完的效果 主要就是在then里面三个状态里面加try catch 并且catch里面的错误手动调用reject函数,并且将错误传递进去

    JavaScript从零到一实现Promise的亿点点细节

    判断 then 里面返回值是普通值 还是一个新的 Promise?

    • 该对象的状态和结果由回调函数的返回值决定
    • 如果返回值是Promise对象:
      • 返回值成功, 新Promise就是成功
      • 返回值失败, 新Primise就是失败
    • 如果返回值不是Promise对象
      • 新Promsie就是成功,它的值就是返回值

    onFulfilled 返回的是普通值,直接resolve, 返回的是promise 的实例let result = onFulfilled(this.value)) 这种if (result instanceof Yu) 是promise 的实例,就调用result.then(resolve, reject)并且传入外层then的 resolve, reject方法 翻译成代码就是下面的意思:

    //成功里面
    if (result instanceof Yu) {
      // result.then(
      //   value => {
      //     resolve(value);
      //   },
      //   reason => {
      //     //处理拒绝里面的返回
      //     reject(reason);
      //   }
      // );
      // 等价于;
      result.then(resolve, reject);
    } else {
      //返回普通值的话 直接改变状态
      resolve(result);
    }
    
    //失败里也要改
    //pending里面也要改
    

    现在的 then 方法

    then(onFulfilled, onRejected) {
              //1  两个参数的校验
              onFulfilled =
                typeof onFulfilled === "function" ? onFulfilled : () => this.value;
              onRejected =
                typeof onRejected === "function" ? onRejected : () => this.value;
              // onRejected =
              //   typeof onRejected === "function"
              //     ? onRejected
              //     : err => {
              //         throw err;
              //       };
              //3.1 新建一个新的实例
              let newThen = new Yu((resolve, reject) => {
                //2  执行两个传进来的函数 是对应的状态的时候 才执行
                if (this.state === Yu.FUIFILLED) {
                  fakeMicroTask(() => {
                    try {
                      let result = onFulfilled(this.value);
                      if (result instanceof Yu) {
                        // result.then(
                        //   value => {
                        //     resolve(value);
                        //   },
                        //   reason => {
                        //     //处理拒绝里面的返回
                        //     reject(reason);
                        //   }
                        // );
                        // 等价于;
                        result.then(resolve, reject);
                      } else {
                        //返回普通值的话 直接改变状态
                        resolve(result);
                      }
                    } catch (e) {
                      onRejected(this.value);
                    }
                  });
                }
    
                if (this.state === Yu.REJECTED) {
                  fakeMicroTask(() => {
                    try {
                      let result = onRejected(this.value);
                      if (result instanceof Yu) {
                        result.then(resolve, reject);
                      } else {
                        //返回普通值的话 直接改变状态
                        resolve(result);
                      }
                    } catch (e) {
                      onRejected(this.value);
                    }
                  });
                }
                if (this.state === Yu.PENDING) {
                  //因为不知道什么时候成功或者失败,所以要暂时将函数保存
                  this.callbacks.push({
                    onFulfilled: value => {
                      try {
                        let result = onFulfilled(value);
                        if (result instanceof Yu) {
                          result.then(resolve, reject);
                        } else {
                          //返回普通值的话 直接改变状态
                          resolve(result);
                        }
                      } catch (err) {
                        onRejected(err);
                      }
                    },
                    onRejected: reason => {
                      try {
                        let result = onRejected(value);
                        if (result instanceof Yu) {
                          result.then(resolve, reject);
                        } else {
                          //返回普通值的话 直接改变状态
                          resolve(result);
                        }
                      } catch (err) {
                        onRejected(err);
                      }
                    }
                  });
                }
              });
              //3.2 返回一个新的promise实例
              return newThen;
            }
    

    上面很多都是重复的,可以把相同部分取出来, 代码重构

         parse(result, resolve, reject) {
              try {
                //去掉这一行 因为成功和失败调用的不一样 在外部传进来
                // let result = onFulfilled(this.value);
                if (result instanceof Yu) {
                  result.then(resolve, reject);
                } else {
                  //返回普通值的话 直接改变状态
                  resolve(result);
                }
              } catch (e) {
                onRejected(this.value);
              }
            }
    
    

    现在整体看起来是这样 调用parse:

    const fakeMicroTask = (cb, timer = 1000) => setTimeout(cb, timer);
    class Yu {
      static PENDING = "pending";
      static FUIFILLED = "fulfilled";
      static REJECTED = "rejected";
      constructor(executor) {
        this.state = Yu.PENDING;
        this.value = null;
        this.callbacks = [];
    
        try {
          executor(this.resolve.bind(this), this.reject.bind(this));
        } catch (e) {
          this.reject(e);
        }
      }
      resolve(value) {
        if (this.state === Yu.PENDING) {
          this.state = Yu.FUIFILLED;
          this.value = value;
    
          fakeMicroTask(() => {
            this.callbacks.forEach(cbObj => {
              cbObj.onFulfilled(value);
            });
          });
        }
      }
      reject(reason) {
        if (this.state === Yu.PENDING) {
          this.state = Yu.REJECTED;
          this.value = reason;
    
          fakeMicroTask(() => {
            this.callbacks.forEach(cbObj => {
              cbObj.onRejected(reason);
            });
          });
        }
      }
    
      parse(result, resolve, reject) {
        try {
          //去掉这一行 因为成功和失败调用的不一样 在外部传进来
          // let result = onFulfilled(this.value);
          if (result instanceof Yu) {
            result.then(resolve, reject);
          } else {
            //返回普通值的话 直接改变状态
            resolve(result);
          }
        } catch (e) {
          onRejected(this.value);
        }
      }
    
       then(onFulfilled, onRejected) {
              //1  两个参数的校验
              onFulfilled =
                typeof onFulfilled === "function" ? onFulfilled : () => this.value;
              onRejected =
                typeof onRejected === "function" ? onRejected : () => this.value;
              //3.1 新建一个新的实例
              let newPromiseInstance = new Yu((resolve, reject) => {
                //2  执行两个传进来的函数 是对应的状态的时候 才执行
                if (this.state === Yu.FUIFILLED) {
                  fakeMicroTask(() => {
                    this.parse(onFulfilled(this.value), resolve, reject);
                  });
                }
    
                if (this.state === Yu.REJECTED) {
                  fakeMicroTask(() => {
                    this.parse(onRejected(this.value), resolve, reject);
                  });
                }
                if (this.state === Yu.PENDING) {
                  //因为不知道什么时候成功或者失败,所以要暂时将函数保存
                  this.callbacks.push({
                    onFulfilled: value => {
                      this.parse(onFulfilled(value), resolve, reject);
                    },
                    onRejected: reason => {
                      this.parse(onRejected(reason), resolve, reject);
                    }
                  });
                }
              });
              //3.2 返回一个新的promise实例
              return newPromiseInstance;
            }
    
    let p = new Yu((resolve, reject) => {
      resolve("success");
    })
      .then(
        value => {
          console.log("first then " + value);
          return "alex from firs then ";
        },
        reason => console.log(reason)
      )
      .then(val => console.log("second " + val));
    

    测试then中的链式操作以及then里面返回普通值的情况

    • 情况1 resolve一个“解决”,第一个then里面成功的回调里面返回普通值,第二个then里面成功的回调打印第一个then里面返回的值

    JavaScript从零到一实现Promise的亿点点细节

    • 情况2 reject一个值, 第一个then里面失败的回调里面返回普通值,在第二个then里面成功的回调里面打印第一个then里面失败的回调里面返回普通值

    JavaScript从零到一实现Promise的亿点点细节

    • 情况3 测试pending状态,就是调用resolve的时候加异步,第一个then成功回调里面返回普通值,第二个then成功回调里面输出上一个then成功回调里面返回的值

    JavaScript从零到一实现Promise的亿点点细节

    • 情况4 测试pending状态,就是调用reject的时候加异步,第一个then失败回调函数里面返回普通值, 第二个then成功回调里面输出上一个then失败回调函数里面返回的值

    JavaScript从零到一实现Promise的亿点点细节

    then 返回promise的处理

    上面处理的都是then返回基本值的情况, 如果返回一个pormise的话, 我们现在的版本是不对的, 要做一下处理, 就是在then里面原来三个状态判断的地方从直接resolve改成先判断上一个的结果是不是promise的实例 是的话用结果的then方法再次传递进去resolve reject ,不是的话,直接用resolve result 看代码: 先看then里面 fulfilled的状态的情况。

    JavaScript从零到一实现Promise的亿点点细节

    简化里面的代码

    JavaScript从零到一实现Promise的亿点点细节

    就变成下面这种写法了

    JavaScript从零到一实现Promise的亿点点细节

    再改造一下 then里面 rejected 和pending的情况的 对then里面返回Promise的处理

    rejected 改造前:

    JavaScript从零到一实现Promise的亿点点细节

    rejected改造后:

    JavaScript从零到一实现Promise的亿点点细节

    pending的情况的改造前,要对两种情况分别处理:

    JavaScript从零到一实现Promise的亿点点细节

    pending 的情况改造后:

    JavaScript从零到一实现Promise的亿点点细节

    现在then方法:

     then(onFulfilled, onRejected) {
              //1  两个参数的校验
              onFulfilled =
                typeof onFulfilled === "function" ? onFulfilled : () => this.value;
              onRejected =
                typeof onRejected === "function" ? onRejected : () => this.value;
    
              //3.1 新建一个新的实例
              let newPromiseInstance = new Yu((resolve, reject) => {
                //2  执行两个传进来的函数 是对应的状态的时候 才执行
                
                //成功的判断以及处理
                if (this.state === Yu.FUIFILLED) {
                  //放在异步队列
                  fakeMicroTask(() => {
                    //增加错误处理
                    try {
                      //对then onFulfilled 返回值是普通值还是新的Promise的处理
                      //取到返回值
                      let result = onFulfilled(this.value);
                      //返回值是 Promise的实例的情况
                      if (result instanceof Yu) {
                        result.then(resolve, reject);
                      } else {
                      //返回值不是 Promise的实例的情况
                        resolve(result);
                      }
                    } catch (e) {
                      //对then里面onFulfilled 或者onRejected 函数里面抛出异常或者直接使用未声明的变量的容错处理,会在下一个then的onRejected里面接收到
                      reject(e);
                    }
                  });
                }
                //失败的判断以及处理
                if (this.state === Yu.REJECTED) {
                  fakeMicroTask(() => {
                    try {
                     //对then onRejected 返回值是普通值还是新的Promise的处理
                      let result = onRejected(this.value);
                      if (result instanceof Yu) {
                        result.then(resolve, reject);
                      } else {
                        resolve(result);
                      }
                    } catch (e) {
                      reject(e);
                    }
                  });
                }
                if (this.state === Yu.PENDING) {
                  //因为不知道什么时候成功或者失败,所以要暂时将函数保存
                  this.callbacks.push({
                    onFulfilled: value => {
                      try {
                      //对then pending的 onFulfilled 返回值是普通值还是新的Promise的处理
                        let result = onFulfilled(this.value);
                        if (result instanceof Yu) {
                          result.then(resolve, reject);
                        } else {
                          resolve(result);
                        }
                      } catch (e) {
                        reject(e);
                      }
                    },
                    onRejected: reason => {
                      try {
                       //对then pending的 onRejected 返回值是普通值还是新的Promise的处理
                        let result = onRejected(this.value);
                        if (result instanceof Yu) {
                          result.then(resolve, reject);
                        } else {
                          resolve(result);
                        }
                      } catch (e) {
                        reject(e);
                      }
                    }
                  });
                }
              });
              //3.2 返回一个新的promise实例
              return newPromiseInstance;
            }
    
    

    then 方法的小重构

    上面三个状态里面的try catch 里面相似内容过多,我们可以封装一个小的函数统一处理:

    parse(result, resolve, reject) {
      try {
        //去掉这一行 因为成功和失败调用的不一样 在外部传进来
        // let result = onFulfilled(this.value);
        if (result instanceof Yu) {
          //then 里面成功或者失败的 回调函数里面返回一个promise的实例的时候 调用then传入外层的resolve reject继续处理
          result.then(resolve, reject);
        } else {
          //返回普通值的话 直接改变状态
          resolve(result);
        }
      } catch (e) {
        reject(this.value);
      }
    }
    

    去掉注释:

    parse(result, resolve, reject) {
      try {
        if (result instanceof Yu) {
          result.then(resolve, reject);
        } else {
          resolve(result);
        }
      } catch (e) {
        reject(this.value);
      }
    }
    
    

    JavaScript从零到一实现Promise的亿点点细节

    接下来对重复部分替换,成parse函数

    JavaScript从零到一实现Promise的亿点点细节

    对then里面返回一个promise的 并且是resolve的状态的改造后的测试

    JavaScript从零到一实现Promise的亿点点细节

    测试对then里面返回一个promise的情况, 并且故意写错的情况

    JavaScript从零到一实现Promise的亿点点细节

    如果将故意写错的一行,放在resovle 后面会输出什么? 原生的Primise的处理是这样的

    JavaScript从零到一实现Promise的亿点点细节 结果是:不会处理, 放前面会处理

    我们实现的Yu也是这样:

    JavaScript从零到一实现Promise的亿点点细节

    同理使用parse函数和继续改造then里面pending 和onRejected的状态里面的重复。

    改造后的rejected:

    JavaScript从零到一实现Promise的亿点点细节 测试rejected里面改造后的正确情况, 这时候 第一个then里面要在第二个参数 也就是onRejected回调函数里返回新的promise

    JavaScript从零到一实现Promise的亿点点细节

    改造前的pending的状态:

    JavaScript从零到一实现Promise的亿点点细节 改造后的:

    JavaScript从零到一实现Promise的亿点点细节

    测试pending resolve 的情况

    JavaScript从零到一实现Promise的亿点点细节

    测试pending reject的情况

    JavaScript从零到一实现Promise的亿点点细节

    改造完pending后的整体的then方法:

    JavaScript从零到一实现Promise的亿点点细节

    对比之前的then方法,代码减少了很多,也更易读

    原生的Promise没有对异步调用resolve 里面出现错误做处理,我们这里先不考虑这个情况了。

    JavaScript从零到一实现Promise的亿点点细节

    Promise 返回类型的约束

    原生Promise的表现:

    JavaScript从零到一实现Promise的亿点点细节 说明then里面的方法是异步执行的 既然是异步的就可以拿到then返回的新的primise 实例p

    JavaScript从零到一实现Promise的亿点点细节

    我们自己的实现里面没有做处理, 输出的结果是这样的:

    JavaScript从零到一实现Promise的亿点点细节

    可以统一在parse函数里面做处理:

    JavaScript从零到一实现Promise的亿点点细节

       parse(newReturnedPromiseInstance, result, resolve, reject) {
              //If promise and x refer to the same object, reject promise with a TypeError as the reason.
              if (newReturnedPromiseInstance === result) {
                throw new TypeError("Chaining cycle detected for promise");
              }
              try {
                if (result instanceof Yu) {
                  result.then(resolve, reject);
                } else {
                  resolve(result);
                }
              } catch (e) {
                reject(this.value);
              }
            }
    

    然后在then里面调用parse的地方做修改:

    JavaScript从零到一实现Promise的亿点点细节

    测试一下 ,发现可以了

    JavaScript从零到一实现Promise的亿点点细节

    5 实现Promise.resolve

    原生Promise 的resolve reject 传入一个基本值的情况

    JavaScript从零到一实现Promise的亿点点细节

    我们的实现:

    static resolve(value) {
      return new Yu((resolve, reject) => {
        resolve(value);
      });
    }
    

    也要考虑传入的是一个promise对象的情况 所以代码改为:

    static resolve(value) {
      return new Yu((resolve, reject) => {
        if (value instanceof Yu) {
          value.then(resolve, reject);
        } else {
          resolve(value);
        }
      });
    }
    

    测试Yu.resolve

    JavaScript从零到一实现Promise的亿点点细节

    6 实现Promise.reject

    同理 Yu.reject

    static reject(reason) {
      return new Yu((resolve, reject) => {
        if (reason instanceof Yu) {
          reason.then(resolve, reject);
        } else {
          reject(reason);
        }
      });
    }
    

    测试Yu.reject

    JavaScript从零到一实现Promise的亿点点细节

    7 实现Promise.all

    所有的成功 才成功 ,所以需要一个数组计数 看结果数组和传入的primises数组是否相同,是的话所有的结果数组作为参数 传入到resolve里面

    代码实现

    JavaScript从零到一实现Promise的亿点点细节

    static all(promisesArr) {
      let values = [];
      return new Yu((resolve, reject) => {
        promisesArr.forEach(promise => {
          promise.then(
            val => {
              values.push(val);
              //相等的时候 才做resolve的处理
              if (values.length == promisesArr.length) {
                resolve(values);
              }
            },
            reason => {
              reject(reason);
            }
          );
        });
      });
    }
    

    测试有一个失败的情况

    JavaScript从零到一实现Promise的亿点点细节

    测试都成功的情况

    JavaScript从零到一实现Promise的亿点点细节

    8 实现Promise.race

    static race(promsisesArr) {
      return new Yu((resolve, reject) => {
        promsisesArr.map(promise => {
          promise.then(
            value => {
              resolve(value);
            },
            reason => {
              reject(reason);
            }
          );
        });
      });
    }
    

    9 测试todo 后面补上

    10 总结:

    • Yu 是一个构造函数,
      • 上面有state存当前状态,默认是pending, 还可以是fulfilled rejected
      • 还有一个 this.value 保存外部传递进来的值
      • 接收一个回调函数 executor 需要做错误处理,这个回调函数里面接收两个参数 是resolve, reject 要注意this绑定的问题,
      • executor在构造函数里面调用,是同步的
      • 构造函数上面还有一个callbacks函数,用来存onFulfilled 和onRejected函数,方便后面resolve 或者reject 函数调用
      • promise状态改变一开始需要手动调用resolve或者reject 分同步和异步两种情况
    • Yu 的原型上面有resovle reject then 方法
      • resolve 方法里面做的事情是有三件事情:
        1. 当前状态是pending的时候,将传入进来的value保存在 this.value上
        2. 当前状态是pending的时候,修改当前状态为fulfilled
        3. 如果当前实例上面callbacks里面有值的话, 就循环它,取出里面的onFulfilled的函数,执行它,并且将resolve的参数传递给他
      • reject 方法里面做的事有三件事:
        1. 当前状态是pending的时候,将传入进来的reason保存在 this.value上
        2. 当前状态是pending的时候,修改当前状态为rejected
        3. 如果当前实例上面callbacks里面有值的话, 就循环它,取出里面的onRejected的函数,执行它,并且将resolve的参数传递给他
      • then 方法
        1. 返回一个新的Promise实例,通过原型链就可以继续使用原型链上面的方法

        2. then的两个参数要做处理

          • 处理错误
          • 处理参数不是函数的情况
          • 处理第二个参数抛出异常的情况
        3. then里面有三种状态需要分别处理 3.1 pending状态,pending状态的回调函数的执行还是在resolve 和reject函数里面。同过循环callbacks执行

           - 3.1.1 onFulfilled 不确定当前状态,所以要保存到之前callbacks上面, 取到 onFulfilled的返回值,try catch 处理 并且判断返回值是不是Promise的实例,是的话,调用结果的then方法,传入resolve ,reject ,否则直接resolve(返回值)
           
           - 3.1.2 onRejected 不确定当前状态,所以要保存到之前callbacks上面,取到 onRejected的返回值,try catch 处理 并且判断返回值是不是Promise的实例,是的话,调用结果的then方法,传入resolve ,reject ,否则直接resolve(返回值)
          

          3.2 fulfilled的状态

           - 放在异步里面,调用resolve
           
          

          3.3 rejected的状态

           - 放在异步里面, 调用reject
          
        4. then里面返回的新的promise不能在下一个then里面是一个的处理,要在异步里面才可以取到上一个实例

        5. 三种状态的错误处理

        6. 重复代码的过抽离

    • Yu构造函数上面有all race resolve reject 方法
      • all : 有一个失败就失败,多个都成功才成功, 多个成功是内部维护一个数组。每个promise.then里面 传入成功的value 到数组内, 最后判断两个数组长度是否相等, 相等的话代表成功,返回有所有异步成功值的数组
      • race : 有一个失败就是失败,有一个成功就改变状态为成功。
      • resolve:
        • 传入的参数是基本值的情况
        • 传入的参数是一个promise实例的情况
      • resolve:
        • 传入的参数是基本值的情况
        • 传入的参数是一个promise实例的情况
      • 实现的过程中 还会有一些细节不完善,比如这里没有实现一些更加细节的边界处理,欢迎指正,

    完整代码

    <!DOCTYPE html>
    <html lang="en">
     <head>
       <meta charset="UTF-8" />
       <meta name="viewport" content="width=device-width, initial-scale=1.0" />
       <meta http-equiv="X-UA-Compatible" content="ie=edge" />
       <title>Document</title>
     </head>
     <body>
       <script>
         const fakeMicroTask = (cb, timer = 1000) => setTimeout(cb, timer);
         class Yu {
           static PENDING = "pending";
           static FUIFILLED = "fulfilled";
           static REJECTED = "rejected";
           constructor(executor) {
             this.state = Yu.PENDING;
             this.value = null;
             this.callbacks = [];
    
             try {
               executor(this.resolve.bind(this), this.reject.bind(this));
             } catch (e) {
               this.reject(e);
             }
           }
           resolve(value) {
             if (this.state === Yu.PENDING) {
               this.state = Yu.FUIFILLED;
               this.value = value;
    
               fakeMicroTask(() => {
                 this.callbacks.forEach(cbObj => {
                   cbObj.onFulfilled(value);
                 });
               });
             }
           }
           reject(reason) {
             if (this.state === Yu.PENDING) {
               this.state = Yu.REJECTED;
               this.value = reason;
    
               fakeMicroTask(() => {
                 this.callbacks.forEach(cbObj => {
                   cbObj.onRejected(reason);
                 });
               });
             }
           }
    
           parse(newReturnedPromiseInstance, result, resolve, reject) {
             //If promise and x refer to the same object, reject promise with a TypeError as the reason.
             if (newReturnedPromiseInstance === result) {
               throw new TypeError("Chaining cycle detected for promise");
             }
             try {
               if (result instanceof Yu) {
                 result.then(resolve, reject);
               } else {
                 resolve(result);
               }
             } catch (e) {
               reject(this.value);
             }
           }
           then(onFulfilled, onRejected) {
             //1  两个参数的校验
             onFulfilled =
               typeof onFulfilled === "function" ? onFulfilled : () => this.value;
             onRejected =
               typeof onRejected === "function" ? onRejected : () => this.value;
             //3.1 新建一个新的实例
             let newPromiseInstance = new Yu((resolve, reject) => {
               //2  执行两个传进来的函数 是对应的状态的时候 才执行
               if (this.state === Yu.FUIFILLED) {
                 fakeMicroTask(() => {
                   this.parse(
                     newPromiseInstance,
                     onFulfilled(this.value),
                     resolve,
                     reject
                   );
                 });
               }
               if (this.state === Yu.REJECTED) {
                 fakeMicroTask(() => {
                   this.parse(
                     newPromiseInstance,
                     onRejected(this.value),
                     resolve,
                     reject
                   );
                 });
               }
               if (this.state === Yu.PENDING) {
                 //因为不知道什么时候成功或者失败,所以要暂时将函数保存
                 this.callbacks.push({
                   onFulfilled: value => {
                     this.parse(
                       newPromiseInstance,
                       onFulfilled(this.value),
                       resolve,
                       reject
                     );
                   },
                   onRejected: reason => {
                     this.parse(
                       newPromiseInstance,
                       onRejected(this.value),
                       resolve,
                       reject
                     );
                   }
                 });
               }
             });
             //3.2 返回一个新的promise实例
             return newPromiseInstance;
           }
    
           static resolve(value) {
             return new Yu((resolve, reject) => {
               if (value instanceof Yu) {
                 value.then(resolve, reject);
               } else {
                 resolve(value);
               }
             });
           }
           static reject(reason) {
             return new Yu((resolve, reject) => {
               if (reason instanceof Yu) {
                 reason.then(resolve, reject);
               } else {
                 reject(reason);
               }
             });
           }
    
           static all(promisesArr) {
             let values = [];
             return new Yu((resolve, reject) => {
               promisesArr.forEach(promise => {
                 promise.then(
                   val => {
                     values.push(val);
                     //相等的时候 才做resolve的处理
                     if (values.length == promisesArr.length) {
                       resolve(values);
                     }
                   },
                   reason => {
                     reject(reason);
                   }
                 );
               });
             });
           }
    
           static race(promsisesArr) {
             return new Yu((resolve, reject) => {
               promsisesArr.map(promise => {
                 promise.then(
                   value => {
                     resolve(value);
                   },
                   reason => {
                     reject(reason);
                   }
                 );
               });
             });
           }
         }
         //测试Yu.all
         let p1 = new Yu(resolve => {
           resolve(1);
         });
         let p2 = new Yu((resolve, reject) => {
           reject("拒绝");
         });
         Yu.all([p1, p2]).then(
           arrs => {
             console.log(arrs);
           },
           err => {
             console.log(err);
           }
         );
    
         // //测试Yu.resolve传入基本值的情况
         // Yu.resolve("test").then(val => {
         //   console.log(val);
         // });
         // //测试Yu.resolve传入promsie的情况
         // let p = new Yu((resolve, reject) => {
         //   resolve("yu  成功");
         // });
         // Yu.resolve(p).then(val => {
         //   console.log(val);
         // });
         // console.log(
         //   Yu.reject("reject  Yu ").then(null, reason => {
         //     console.log(reason);
         //   })
         // );
    
         //测试Yu.reject传入基本值的情况
         // Yu.reject("test").then(null, val => {
         //   console.log(val);
         // });
         // //测试Yu.reject传入promsie的情况
         // let p = new Yu((resolve, reject) => {
         //   reject("yu  拒绝");
         // });
         // Yu.reject(p).then(null, err => {
         //   console.log(err);
         // });
    
         //test diy Promsie
         // let p = new Yu((resolve, reject) => {
         //   setTimeout(() => {
         //     reject("拒绝");
         //   }, 1000);
         // })
         //   .then(
         //     value => {
         //       console.log(value);
         //       return new Yu((resolve, reject) => {
         //         resolve("Yu first then resolve");
         //       });
         //     },
         //     reason => {
         //       console.log(reason);
         //       return new Yu((resolve, reject) => {
         //         console.log(aaaa);
         //         resolve("Yu first then reject");
         //       });
         //     }
         //   )
         //   .then(
         //     val => console.log(val),
         //     reason => {
         //       console.log("second then  rejected function " + reason);
         //     }
         //   );
    
         //test chain promise
         // let p1 = new Yu(resolve => {
         //   resolve(222);
         // });
         // let p2 = p1.then(val => {
         //   return p2;
         // });
         // let p = new Promise((resolve, reject) => {
         //   resolve("111");
         // });
         // let p2 = p.then(val => {
         //   return p2;
         // });
         // console.log(p2);
    
         //test promise
         // let p2 = new Promise((resolve, reject) => {
         //   setTimeout(() => {
         //     console.log(AAA);
         //     resolve("success");
         //   }, 1000);
         // })
         //   .then(
         //     val => {
         //       console.log(val);
         //       return new Promise((resolve, reject) => {
         //         resolve("first then resolve");
         //       });
         //     },
         //     reason => {}
         //   )
         //   .then(
         //     val => {
         //       console.log(val);
         //     },
         //     reason => {
         //       console.log(reason);
         //     }
         //   );
    
         //test Promsie.resolve Promise.reject
         // let p1 = new Promise(resolve => {
         //   resolve("成功");
         // });
         // Promise.resolve(p1).then(val => {
         //   console.log(val);
         // });
         // let p2 = new Promise((resolve, reject) => {
         //   reject("拒绝");
         // });
         // Promise.reject(p2).then(
         //   val => {
         //     console.log(val);
         //   },
         //   reason => {
         //     console.log(reason);
         //   }
         // );
    
         // test Promise.all
         // let p11 = new Promise(resolve => {
         //   resolve(111);
         // });
         // let p22 = new Promise((resolve, reject) => {
         //   // reject(2);
         //   resolve(222);
         // });
         // Promise.all([p11, p22]).then(
         //   arrs => {
         //     console.log(arrs);
         //   },
         //   err => {
         //     console.log(err);
         //   }
         // );
       </script>
     </body>
    </html>
    
    

    致谢

    • 能看到这里,说明是真的认真读了,感谢!

    起源地下载网 » JavaScript从零到一实现Promise的亿点点细节

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元