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

679 阅读20分钟

image.png

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 的处理

image.png

image.png

  • 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函数,并且将错误传递进去

image.png

判断 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里面返回普通值的情况

开始的Promise的状态的改变都需要new Promsie的里面手动调用resove 或者reject 才可以让状态开始流动

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

image.png

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

image.png

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

image.png

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

image.png

then 返回promise的处理

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

image.png

简化里面的代码

image.png

就变成下面这种写法了

image.png

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

rejected 改造前:

image.png

rejected改造后:

image.png

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

image.png

pending 的情况改造后:

image.png

现在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);
  }
}

image.png

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

image.png

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

image.png

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

image.png

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

image.png 结果是:不会处理, 放前面会处理

我们实现的Yu也是这样:

image.png

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

改造后的rejected:

image.png 测试rejected里面改造后的正确情况, 这时候 第一个then里面要在第二个参数 也就是onRejected回调函数里返回新的promise

image.png

改造前的pending的状态:

image.png 改造后的:

image.png

测试pending resolve 的情况

image.png

测试pending reject的情况

image.png

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

image.png

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

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

image.png

Promise 返回类型的约束

If promise and x refer to the same object, reject promise with a TypeError as the reason.

原生Promise的表现:

image.png 说明then里面的方法是异步执行的 既然是异步的就可以拿到then返回的新的primise 实例p

image.png

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

image.png

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

image.png

   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的地方做修改:

image.png

测试一下 ,发现可以了

image.png

5 实现Promise.resolve

js es6 加static 就是往构造函数上定义方法或者属性, 可以通过构造函数.方法名直接取到,比如 class Yu{static resolve(value) {}} 取的话就用 Yu.resolve

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

image.png

我们的实现:

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

image.png

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

image.png

7 实现Promise.all

思路 返回一个新的Promise 并且有任何一个失败就失败 ,就是直接then的失败的里面reject即可, 所有的成功 才成功 ,所以需要一个数组计数 看结果数组和传入的primises数组是否相同,是的话所有的结果数组作为参数 传入到resolve里面

代码实现

image.png

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

测试有一个失败的情况

image.png

测试都成功的情况

image.png

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>

致谢

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