手写Promise

Posted 还是不会呀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写Promise相关的知识,希望对你有一定的参考价值。

手写Promise代码

const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";
const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";
const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";
// 对resolve里的回调函数进行监听抛出错误的同时,需要对回调函数的返回值进行判断
// 参考原生Promise返回值的三种情况
function fulfilledFunctionThrowErr(onfulfilled, query, resolve, reject) {
  try {
    const result = exceFn(query);
    if (result instanceof MsiPromise || "then" in result) {
      result.then((res) => {
        resolve(res);
      });
    } else {
      resolve(result);
    }
  } catch (error) {
    reject(error);
  }
}
// 但是reject内的回调函数返回值却没有这样的区别,返回什么类型就是什么类型
function rejectededFunctionThrowErr(onrejected, query, resolve, reject) {
  try {
    const result = exceFn(query);
    resolve(result);
  } catch (error) {
    reject(error);
  }
}
class MsiPromise {
  constructor(executor) {
    // 用于保存当前Promise对象的状态
    this.status = PROMISE_STATUS_PENDING;
    // 因为同一个Promise可以使用多个then和catch方法的回调函数,那么需要将其保存起来
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];
    // resolve函数
    const resolve = (value) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        this.value = value;
        // 微任务队列,如果不使用那么此时,代码是还没有执行到then那一步去的,
        // 对应的回调函数也是无法放入到数组内,使用微任务队列,涉及到事件循环[后面将会讲到]
        queueMicrotask(() => {
          // 之所以会有下面两句,因为微任务队列代码稍后执行,若创建Promise的回调函数内都调用了resolve和reject
          // 本应该是先写到那个就执行那个,并且状态改变后且不可再改变,那么resolve,reject函数内的微任务队列内的回调函数
          // 都将加入微任务队列,那么就有下面第一句,不是pending状态直接返回,这样就可以执行一次
          if (this.status !== PROMISE_STATUS_PENDING) return;
          this.status = PROMISE_STATUS_FULFILLED;
          this.onFulfilledCallbacks.forEach((fn) => {
            fn(this.value);
          });
        });
      }
    };
    // reject函数
    const reject = (reason) => {
      if (this.status !== PROMISE_STATUS_PENDING) return;
      if (this.status === PROMISE_STATUS_PENDING) {
        this.reason = reason;
        queueMicrotask(() => {
          this.status = PROMISE_STATUS_REJECTED;
          this.onRejectedCallbacks.forEach((fn) => {
            fn(this.reason);
          });
        });
      }
    };
    // 创建Promise对象时,传入的回调函数,是直接执行的
    // 同时也可以在该回调函数内抛出错误,在catch回调内捕获到,就需要try...catch进行捕获
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  then(onfulfilled, onrejected) {
    // 之所以要进行这样一层判断,因为catch方法,finally方法都是基于then方法实现的
    // 对于如下情况:
    // new MsiPromise((resolve,reject)=>{reject("err message") }).then(res=>{ }).catch(err=>{})
    // catch和前面的then是隶属于不同的promsie对象,也就是需要再then内将 第一个promise对象错误抛出,
    // 才能在 catch内捕获到错误,同理也需要将 fulfilled的值直接返回给下一个promise
    onrejected =
      onrejected ??
      ((err) => {
        throw err;
      });
    onfulfilled = onfulfilled ?? ((res) => res);
    // then的返回值其实也是一个Promsie对象
    return new MsiPromise((resolve, reject) => {
      if (this.status === PROMISE_STATUS_FULFILLED) {
        // 这是再加入数组内的函数已经执行之后,又出现一个then方法内的回调
        // 那么此时不需要再加入对应数组内,直接执行就行了
        fulfilledFunctionThrowErr(onfulfilled, this.value, resolve, reject);
      }
      if (this.status === PROMISE_STATUS_REJECTED) {
        rejectededFunctionThrowErr(onrejected, this.reason, resolve, reject);
      }
      if (this.status === PROMISE_STATUS_PENDING) {
        this.onFulfilledCallbacks.push(() => {
          fulfilledFunctionThrowErr(onfulfilled, this.value, resolve, reject);
        });
        this.onRejectedCallbacks.push(() => {
          rejectededFunctionThrowErr(onrejected, this.reason, resolve, reject);
        });
      }
    });
  }
  catch(onrejected) {
    return this.then(undefined, onrejected);
  }
  finally(onfinally) {
    this.then(onfinally, onfinally);
  }

  static resolve(res) {
    return new MsiPromise((resolve) => {
      resolve(res);
    });
  }
  static reject(reason) {
    return new MsiPromise((resolve) => {
      resolve(reason);
    });
  }
  static all(promises) {
    return new MsiPromise((resolve, reject) => {
      const result = [];
      promises.forEach((promise) => {
        promise.then(
          (res) => {
            result.push(res);
            if (result.length === promises.length) {
              resolve(result);
            }
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }

  static allSettled(promises) {
    return new MsiPromise((resolve, reject) => {
      const result = [];
      promises.forEach((promise) => {
        promise.then(
          (res) => {
            result.push({ status: "fulfilled", value: res });
            if (promises.length === result.length) {
              resolve(result);
            }
          },
          (reason) => {
            result.push({ status: "rejected", value: reason });
            if (promises.length === result.length) {
              reject(result);
            }
          }
        );
      });
    });
  }

  static any(promises) {
    return new MsiPromise((resolve, reject) => {
      const result = [];
      promises.forEach((promise) => {
        promise.then(
          (res) => {
            resolve(res);
          },
          (reason) => {
            result.push(reason);
            if (result.length === promises.length) {
              reject(new AggregateError(result));
            }
          }
        );
      });
    });
  }

  static race(promises) {
    return new MsiPromise((resolve) => {
      promises.forEach((promise) => {
        promise.then(
          (res) => {
            resolve(res);
          },
          (reason) => {
            resolve(reason);
          }
        );
      });
    });
  }
}

// 测试代码
MsiPromise.resolve("aaaa").then((res) => {
  console.log(res);
});
MsiPromise.reject("aaaa").then((res) => {
  console.log(res);
});
const p1 = new MsiPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("aaaa");
  }, 1000);
});
const p2 = new MsiPromise((resolve, reject) => {
  setTimeout(() => {
    reject("bbbb");
  }, 2000);
});
const p3 = new MsiPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("cccc");
  }, 3000);
});
MsiPromise.all([p1, p2, p3])
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });

过程思考

在完成上方代码时,发现不使用数组进行保存相应的回调函数,一样可以完成对于的功能,就是在resolve或reject时直接保存对应值,然后每次在then调用时直接传入相应值就行了,一样可以解决then方法延迟调用,并且创建Promise时resolve和reject同时存在只先执行一次得问题。

**但是:**then内得回调和catch内得回调函数并不是同步执行的,ES6中Promise确实也是异步执行的。

同步Promsie执行代码

const PROMISE_STATUS_FULFILLED = "PROMISE_STATUS_FULFILLED";
const PROMISE_STATUS_PENDING = "PROMISE_STATUS_PENDING";
const PROMISE_STATUS_REJECTED = "PROMISE_STATUS_REJECTED";

class MsiPromise {
  constructor(executor) {
    this.status = PROMISE_STATUS_PENDING;
    this.value = null;
    this.reason = null;
    const resolve = (value) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        this.status = PROMISE_STATUS_FULFILLED;
        this.value = value;
      }
    };
    const reject = (reason) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        this.status = PROMISE_STATUS_REJECTED;
        this.reason = reason;
      }
    };
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  then(onfulfilled, onrejected) {
    onrejected =
      onrejected ??
      ((err) => {
        throw err;
      });
    onfulfilled = onfulfilled ?? ((res) => res);
    return new MsiPromise((resolve, reject) => {
      if (this.status === PROMISE_STATUS_FULFILLED) {
        try {
          const result = onfulfilled(this.value);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }
      if (this.status === PROMISE_STATUS_REJECTED) {
        try {
          const error = onrejected(this.reason);
          resolve(error);
        } catch (error) {
          reject(error);
        }
      }
    });
  }
  catch(onrejected) {
    return this.then(undefined, onrejected);
  }
  finally(onfinally) {
    this.then(onfinally, onfinally);
  }
}
// 测试代码
const promise = new MsiPromise((resolve, reject) => {
  console.log("pending");
  resolve("aaa");
});

promise.then((res) => {
  console.log(res);
});

console.log("------------");

以上是关于手写Promise的主要内容,如果未能解决你的问题,请参考以下文章

手写Promise

手写Promise

手写 Promise

手写Promise

VSCode自定义代码片段12——JavaScript的Promise对象

VSCode自定义代码片段12——JavaScript的Promise对象