高级前端
js
【Q022】如何实现一个简单的 Promise

如何实现一个简单的 Promise

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 23 (opens in a new tab)

Author 回答者: shfshanyue (opens in a new tab)

一个简单的 Promise 的粗糙实现,关键点在于

  1. pending 时, thenable 函数由一个队列维护
  2. 当状态变为 resolved(fulfilled) 时,队列中所有 thenable 函数执行
  3. resolved 时, thenable 函数直接执行

rejected 状态同理

class Prom {
  static resolve(value) {
    if (value && value.then) {
      return value;
    }
    return new Prom((resolve) => resolve(value));
  }
 
  constructor(fn) {
    this.value = undefined;
    this.reason = undefined;
    this.status = "PENDING";
 
    // 维护一个 resolve/pending 的函数队列
    this.resolveFns = [];
    this.rejectFns = [];
 
    const resolve = (value) => {
      // 注意此处的 setTimeout
      setTimeout(() => {
        this.status = "RESOLVED";
        this.value = value;
        this.resolveFns.forEach(({ fn, resolve: res, reject: rej }) =>
          res(fn(value)),
        );
      });
    };
 
    const reject = (e) => {
      setTimeout(() => {
        this.status = "REJECTED";
        this.reason = e;
        this.rejectFns.forEach(({ fn, resolve: res, reject: rej }) =>
          rej(fn(e)),
        );
      });
    };
 
    fn(resolve, reject);
  }
 
  then(fn) {
    if (this.status === "RESOLVED") {
      const result = fn(this.value);
      // 需要返回一个 Promise
      // 如果状态为 resolved,直接执行
      return Prom.resolve(result);
    }
    if (this.status === "PENDING") {
      // 也是返回一个 Promise
      return new Prom((resolve, reject) => {
        // 推进队列中,resolved 后统一执行
        this.resolveFns.push({ fn, resolve, reject });
      });
    }
  }
 
  catch(fn) {
    if (this.status === "REJECTED") {
      const result = fn(this.value);
      return Prom.resolve(result);
    }
    if (this.status === "PENDING") {
      return new Prom((resolve, reject) => {
        this.rejectFns.push({ fn, resolve, reject });
      });
    }
  }
}
 
Prom.resolve(10)
  .then((o) => o * 10)
  .then((o) => o + 10)
  .then((o) => {
    console.log(o);
  });
 
return new Prom((resolve, reject) => reject("Error")).catch((e) => {
  console.log("Error", e);
});

Author 回答者: heretic-G (opens in a new tab)

function MyPromise(executor) {
  if (typeof executor !== "function") {
    // throw new Error('Promise resolver 1 is not a function')
  }
  if (this instanceof MyPromise) {
    // throw new Error(`${this} is not a promise`)
  }
  this.PromiseState = "pending";
  this.PromiseFulfillReactions = [];
  this.PromiseRejectReactions = [];
  this.PromiseIsHandled = false;
  this.AlreadyResolved = false;
 
  let resolve = _Resolve(this);
  let reject = _Reject(this);
 
  try {
    executor(resolve, reject);
  } catch (e) {
    reject(e);
  }
}
 
MyPromise.prototype.then = function (onFulfilled, onRejected) {
  let promise = this;
  let capability = NewPromiseCapability();
  return PerformPromiseThen(promise, onFulfilled, onRejected, capability);
};
 
function _Resolve(promise) {
  return function __Resolve(resolution) {
    if (promise.AlreadyResolved) {
      return undefined;
    }
    promise.AlreadyResolved = true;
    if (resolution === promise) {
      return RejectPromise(promise, TypeError("is same"));
    }
    if (
      (typeof resolution !== "function" && typeof resolution !== "object") ||
      resolution === null
    ) {
      return FulfillPromise(promise, resolution);
    }
    let then;
    try {
      then = resolution.then;
    } catch (e) {
      return RejectPromise(promise, e);
    }
    if (typeof then !== "function") {
      return FulfillPromise(promise, resolution);
    } else {
      let job = NewPromiseResolveThenableJob(promise, resolution, then);
      HostEnqueuePromiseJob(job);
    }
    return undefined;
  };
}
 
function _Reject(promise) {
  return function __Reject(reason) {
    if (promise.AlreadyResolved) {
      return undefined;
    }
    promise.AlreadyResolved = true;
    RejectPromise(promise, reason);
  };
}
 
function executor(resolve, reject) {
  this.resolve = resolve;
  this.reject = reject;
}
 
function NewPromiseCapability() {
  let capability = {
    resolve: undefined,
    reject: undefined,
    promise: undefined,
  };
  capability.promise = new MyPromise(executor.bind(capability));
  return capability;
}
 
function PerformPromiseThen(
  promise,
  onFulfilled,
  onRejected,
  resultCapability,
) {
  let fulfillReaction = {
    Capability: resultCapability,
    Type: "Fulfill",
    Handler: onFulfilled,
  };
  let rejectReaction = {
    Capability: resultCapability,
    Type: "Reject",
    Handler: onRejected,
  };
  if (promise.PromiseState === "pending") {
    promise.PromiseFulfillReactions.push(fulfillReaction);
    promise.PromiseRejectReactions.push(rejectReaction);
  } else if (promise.PromiseState === "fulfilled") {
    let resolution = promise.PromiseResult;
    let job = NewPromiseReactionJob(fulfillReaction, resolution);
    HostEnqueuePromiseJob(job);
  } else {
    if (!promise.PromiseIsHandled) {
    }
    let reason = promise.PromiseResult;
    let job = NewPromiseReactionJob(rejectReaction, reason);
    HostEnqueuePromiseJob(job);
  }
  promise.PromiseIsHandled = true;
  if (!resultCapability) return undefined;
  return resultCapability.promise;
}
 
function FulfillPromise(promise, resolution) {
  if (promise.PromiseState !== "pending") {
    return undefined;
  }
  let reactions = promise.PromiseFulfillReactions;
  promise.PromiseResult = resolution;
  promise.PromiseRejectReactions = [];
  promise.PromiseFulfillReactions = [];
  promise.PromiseState = "fulfilled";
  TriggerPromiseReactions(reactions, resolution);
}
 
function RejectPromise(promise, reason) {
  if (promise.PromiseState !== "pending") {
    return undefined;
  }
  let reactions = promise.PromiseRejectReactions;
  promise.PromiseResult = reason;
  promise.PromiseRejectReactions = [];
  promise.PromiseFulfillReactions = [];
  promise.PromiseState = "rejected";
  if (!promise.PromiseIsHandled) {
  }
  TriggerPromiseReactions(reactions, reason);
}
 
function TriggerPromiseReactions(reactions, argument) {
  reactions.forEach((curr) => {
    let job = NewPromiseReactionJob(curr, argument);
    HostEnqueuePromiseJob(job);
  });
}
 
function NewPromiseReactionJob(reaction, argument) {
  return function () {
    let capability = reaction.Capability;
    let type = reaction.Type;
    let handler = reaction.Handler;
    let handlerResult;
    let isError = false;
    if (typeof handler !== "function") {
      if (type === "Fulfill") {
        handlerResult = argument;
      } else {
        isError = true;
        handlerResult = argument;
      }
    } else {
      try {
        handlerResult = handler(argument);
      } catch (e) {
        isError = true;
        handlerResult = e;
      }
    }
    if (!capability) return undefined;
    let status;
    if (!isError) {
      status = capability.resolve(handlerResult);
    } else {
      status = capability.reject(handlerResult);
    }
    return status;
  };
}
 
function NewPromiseResolveThenableJob(promiseToResolve, thenable, then) {
  return function () {
    let resolve = _Resolve(promiseToResolve);
    let reject = _Reject(promiseToResolve);
    promiseToResolve.AlreadyResolved = false;
    let result;
    try {
      result = then.call(thenable, resolve, reject);
    } catch (e) {
      return reject(e);
    }
    return result;
  };
}
 
function HostEnqueuePromiseJob(job) {
  setTimeout(job, 0);
}
 
MyPromise.deferred = function () {
  let dfd = {};
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};
 
module.exports = MyPromise;

Author 回答者: hsq777 (opens in a new tab)

一个简单的 Promise 的粗糙实现,关键点在于

  1. pending 时, thenable 函数由一个队列维护
  2. 当状态变为 resolved(fulfilled) 时,队列中所有 thenable 函数执行
  3. resolved 时, thenable 函数直接执行

rejected 状态同理

class Prom {
  static resolve(value) {
    if (value && value.then) {
      return value;
    }
    return new Prom((resolve) => resolve(value));
  }
 
  constructor(fn) {
    this.value = undefined;
    this.reason = undefined;
    this.status = "PENDING";
 
    // 维护一个 resolve/pending 的函数队列
    this.resolveFns = [];
    this.rejectFns = [];
 
    const resolve = (value) => {
      // 注意此处的 setTimeout
      setTimeout(() => {
        this.status = "RESOLVED";
        this.value = value;
        this.resolveFns.forEach(({ fn, resolve: res, reject: rej }) =>
          res(fn(value)),
        );
      });
    };
 
    const reject = (e) => {
      setTimeout(() => {
        this.status = "REJECTED";
        this.reason = e;
        this.rejectFns.forEach(({ fn, resolve: res, reject: rej }) =>
          rej(fn(e)),
        );
      });
    };
 
    fn(resolve, reject);
  }
 
  then(fn) {
    if (this.status === "RESOLVED") {
      const result = fn(this.value);
      // 需要返回一个 Promise
      // 如果状态为 resolved,直接执行
      return Prom.resolve(result);
    }
    if (this.status === "PENDING") {
      // 也是返回一个 Promise
      return new Prom((resolve, reject) => {
        // 推进队列中,resolved 后统一执行
        this.resolveFns.push({ fn, resolve, reject });
      });
    }
  }
 
  catch(fn) {
    if (this.status === "REJECTED") {
      const result = fn(this.value);
      return Prom.resolve(result);
    }
    if (this.status === "PENDING") {
      return new Prom((resolve, reject) => {
        this.rejectFns.push({ fn, resolve, reject });
      });
    }
  }
}
 
Prom.resolve(10)
  .then((o) => o * 10)
  .then((o) => o + 10)
  .then((o) => {
    console.log(o);
  });
 
return new Prom((resolve, reject) => reject("Error")).catch((e) => {
  console.log("Error", e);
});

catch里面应该是return Prom.reject(result)

Author 回答者: hefeng6500 (opens in a new tab)

话不多说,看码!

// 1、基本架构:
//   状态
//   then
//   执行器函数 executor
 
// 2、executor、resolve、reject
// 3、then 同步下调用
// 4、then 异步下调用
// 5、then 链式调用
//   返回 Promise
//   then 函数递归返回常量结果,供下个 then 使用
//   考虑 then 成功的回调为 null 的情况
 
class Promise {
  static PENDING = "pending";
  static RESOLVED = "resolved";
  static REJECTED = "rejected";
 
  static resolve(value) {
    return new Promise((resolve, reject) => {
      resolve(value);
    });
  }
 
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  }
 
  constructor(executor) {
    this.state = Promise.PENDING;
    this.value = undefined;
    this.reason = undefined;
 
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
 
    const resolve = (value) => {
      if (value instanceof Promise) {
        return value.then(resolve, reject);
      }
 
      if (this.state === Promise.PENDING) {
        this.state = Promise.RESOLVED;
        this.value = value;
 
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };
    const reject = (reason) => {
      this.state = Promise.REJECTED;
      this.reason = reason;
      this.onRejectedCallbacks.forEach((fn) => fn());
    };
 
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
 
  then(onFulfilled, onRejected) {
    onFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason;
          };
 
    let promise = new Promise((resolve, reject) => {
      if (this.state === Promise.PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          });
        });
      }
 
      if (this.state === Promise.RESOLVED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
 
      if (this.state === Promise.REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
    });
 
    return promise;
  }
 
  catch(onRejected) {
    return this.then(null, onRejected);
  }
 
  all(arr) {
    let count = 0;
    let result = [];
 
    return new Promise((resolve, reject) => {
      for (let i = 0; i < arr.length; i++) {
        Promise.resolve(arr[i])
          .then((res) => {
            result[i] = res;
            if (++count === arr.length) {
              resolve(res);
            }
          })
          .catch((error) => {
            reject(error);
          });
      }
    });
  }
 
  race(arr) {
    return new Promise((resolve, reject) => {
      arr.forEach((item) => Promise.resolve(item).then(resolve, reject));
    });
  }
 
  finally(callback) {
    return this.then(
      (value) => {
        return Promise.resolve(callback()).then(() => value);
      },
      (reason) => {
        return Promise.resolve(callback()).then(() => {
          throw reason;
        });
      },
    );
  }
 
  allSettled(arr) {
    let count = 0;
    let result = [];
 
    return new Promise((resolve, reject) => {
      const fn = (i, data) => {
        if (count === arr.length) {
          resolve(result);
        }
 
        result[i] = data;
        count++;
      };
 
      for (let i = 0; i < arr.length; i++) {
        Promise.resolve(arr[i])
          .then((res) => {
            fn(i, { status: "fulfilled", value: res });
          })
          .catch((error) => {
            fn(i, { status: "rejected", reason: error });
          });
      }
    });
  }
 
  // from Node Util.promisify
  promisify(f) {
    return function (...args) {
      return new Promise((resolve, reject) => {
        function callback(error, result) {
          if (error) {
            reject(error);
          } else {
            resolve(result);
          }
        }
 
        args.push(callback);
 
        f.call(this, ...args);
      });
    };
  }
 
  // from Node Util.promisifyAll
  promisifyAll(obj) {
    for (let key in obj) {
      if (typeof obj[key] === "function") {
        obj[key] = this.promisify(obj[key]);
      }
    }
  }
}
 
function resolvePromise(promise, x, resolve, reject) {
  // let promise = new Promise((resolve) => {
  //   resolve(1);
  // }).then((res) => {
  //   return promise;
  // });
 
  if (x === promise) {
    throw TypeError("循环引用");
  }
 
  if ((typeof x === "object" && x !== null) || typeof x === "function") {
    let called;
 
    try {
      let then = x.then;
 
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(promise, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          },
        );
      } else {
        // x: { then: {} }
        if (called) return;
        called = true;
        resolve(x);
      }
    } catch (error) {
      if (called) return;
      called = true;
      reject(error);
    }
  } else {
    // 返回了常量,直接 resolve
    resolve(x);
  }
}
 
const p = new Promise((resolve, reject) => {
  reject(1);
});
 
p.catch((error) => {
  console.log("error + ", error);
  return error;
}).then((res) => {
  console.log(res);
});
 
Promise.deferred = function () {
  let dfd = {};
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject;
  });
  return dfd;
};
 
module.exports = Promise;

Author 回答者: Yu-Lxy (opens in a new tab)

在作者代码基础上做了一些修改 基本功能够用了

class myPromise {
  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECTED = "rejected";
 
  static resolve(value) {
    if (value && value.then) {
      return value;
    }
    return new myPromise((resolve) => resolve(value));
  }
 
  static reject(value) {
    return new myPromise((_, reject) => reject(value));
  }
 
  constructor(fn) {
    this.status = myPromise.PENDING;
    this.result = null;
 
    this.resFns = [];
    this.rejFns = [];
 
    const resolve = (value) => {
      if (this.status === myPromise.PENDING) {
        setTimeout(() => {
          this.status = myPromise.FULFILLED;
          this.result = value;
          this.resFns.forEach(({ fn, resolve, reject }) => resolve(fn(value)));
        });
      }
    };
 
    const reject = (reason) => {
      if (this.status === myPromise.PENDING) {
        setTimeout(() => {
          this.status = myPromise.REJECTED;
          this.result = reason;
          this.rejFns.forEach(({ fn, resolve, reject }) => reject(fn(reason)));
        });
      }
    };
 
    try {
      fn(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
 
  then(resFn, rejFn) {
    resFn = typeof resFn === "function" ? resFn : (value) => value;
    rejFn = typeof rejFn === "function" ? rejFn : (reason) => reason;
 
    const _promise = {
      [myPromise.PENDING]: () => {
        return new myPromise((resolve, reject) => {
          this.resFns.push({ fn: resFn, resolve, reject });
          this.rejFns.push({ fn: rejFn, resolve, reject });
        });
      },
      [myPromise.FULFILLED]: () => myPromise.resolve(resFn(this.result)),
      [myPromise.REJECTED]: () => myPromise.reject(rejFn(this.result)),
    }[this.status];
 
    return _promise();
  }
 
  catch(fn) {
    return this.then(undefined, fn);
  }
 
  finally(cb) {
    return this.then(cb, cb);
  }
}