NsNe / blog

4 stars 0 forks source link

手动实现 Promise #13

Open NsNe opened 3 years ago

NsNe commented 3 years ago

Promise 代表了一个异步操作的最终完成或者失败

定义状态

const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

构造函数定义


function MyPromise(excutor){
  // 初始状态为 pending
  this.status = PENDING;
  // 成功的值
  this.value = undefined;
  // 失败的原因
  this.reason = undefined;
  // then 的回调函数集合,后面会用到
  this.fulfilledCallback = [];
  this.rejectedCallback = [];

  const resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.value = value;
      // 状态变更后执行 then 的回调函数
      this.fulfilledCallback.forEach(fn => fn(value))
    }
  }

  const reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = reason;
      this.rejectedCallback.forEach(fn => fn(reason))
    }
  }

  try {
    excutor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}

原型方法

MyPromise.prototype.then = function(onResolve, onReject){

  onResolve = onResolve === undefined ? value => value : onResolve,
  onReject = onReject === undefined ? error => { throw error } : onReject;

  return new MyPromise((resolve, reject) => {

    function handle(func, value){
      try {
        const result = func(value);
        // 如果返回一个 Promise
        if (result instanceof MyPromise) {
            result.then(resolve, reject);
        } else {
          resolve(result);
        }
      } catch (err) {
          reject(err);
      }
    }

    if (this.status === FULFILLED) {
      queueMicrotask(() => {
        handle(onResolve, this.value);
      });
    }

    if (this.status === REJECTED) {
      queueMicrotask(() => {
        handle(onReject, this.reason);
      });
    }

     // 状态为 pending,将回调函数保存到前面定义的函数集合中
    if (this.status === PENDING) {
      // 保存到 fulfilledCallback
      this.fulfilledCallback.push(value => {
        queueMicrotask(() => {
          handle(onResolve, value);
        });
      });

      // 保存到 rejectedCallback
      this.rejectedCallback.push(reason => {
        queueMicrotask(() => {
          handle(onReject, reason)
        });
      });
    }
  })
};

  MyPromise.prototype.catch = function (onReject) {
    return this.then(undefined, onReject);
  };

  MyPromise.prototype.finally = function (cb) {
    return this.then(
      (value) => MyPromise.resolve(cb()).then(() => value),
      (reason) =>
        MyPromise.resolve(cb()).then(() => {
          throw reason;
        })
    );
  };

静态方法

  MyPromise.resolve = function (value) {
    // 如果参数是MyPromise实例,直接返回这个实例
    if (value instanceof MyPromise) return value;
    return new MyPromise((resolve) => resolve(value));
  };

  MyPromise.reject = function (value) {
    // 如果参数是MyPromise实例,直接返回这个实例
    if (value instanceof MyPromise) return value;
    return new MyPromise((resolve, reject) => reject(value));
  };

  MyPromise.race = function (promises) {
    return new Promise((resolve, reject) => {
      promises.forEach((p) => {
        MyPromise.resolve(p).then(resolve, reject);
      });
    });
  };

  MyPromise.all = function (promises) {
    let result = [];
    const len = promises.length;
    let count = 0;
    return new MyPromise((resolve, reject) => {
      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(
          (res) => {
            result[index] = res;
            count++;
            if (count === len) {
              resolve(result);
            }
          },
          (error) => {
            reject(error);
          }
        );
      });
    });
  };

  MyPromise.allSettled = function (promises) {
    let result = [];
    const len = promises.length;
    let count = 0;
    return new MyPromise((resolve, reject) => {
      promises.forEach((p, index) => {
        MyPromise.resolve(p).then(
          (value) => {
            result[index] = {
              status: FULFILLED,
              value,
            };
            count++;
            if (count === len) {
              resolve(result);
            }
          },
          (reason) => {
            result[index] = {
              status: REJECTED,
              reason,
            };
            count++;
            if (count === len) {
              resolve(result);
            }
          }
        );
      });
    });
  };

最后的最后

上述代码用到了queueMicrotask()来执行微任务 mdn上也提供了queueMicrotask()的polyfill实现 它通过使用立即 resolve 的 promise 创建一个微任务(microtask) 如下:

if (typeof window.queueMicrotask !== "function") {
window.queueMicrotask = function (callback) {
Promise.resolve()
.then(callback)
.catch(e => setTimeout(() => { throw e; }));
};
}