Skip to content

手写 promise

promise 基本特征

  1. 有三个状态:pengding、fullfilled、rejected
  2. new Promise 时,需要传递一个回调,立即执行
  3. 回调接受两个参数,resolve 和 reject
  4. 默认状态是 pending
  5. promise 有一个 value 保存成功状态的值,可以是 undefined、thenable、promise
  6. promise 有一个 reason 保存失败状态的值
  7. promise 智能从 pending 到 rejected 或者 fullfilled,状态一旦确认就不会再改变
  8. promise 必须有一个 then 方法,接受两个参数,分别是成功的回调 onFulfilled 和失败的回调 onRejected
  9. 如果调用 then,promise 已经成功,则执行 onFullfilled,参数是 promise 的 value
  10. 如果调用 then,promise 已经失败,调用 onRejected,参数是 promise 的 reason
  11. 如果 then 抛出异常,那么就把异常作为参数,传给下一个 then 的失败的回调 onRejected
  12. then的参数onFulfilled和onRejected可以缺省,如果不是函数,就忽略,且依旧可以在下面的then中获取到之前返回的值
  13. promise可以then多次,每次都返回新的promise
  14. 如果promise返回一个普通值,就把这个结果作为参数,传递给下一个then的成功回调
  15. 如果promise抛出异常,那么这个异常作为参数传递给下一个then的失败回调
  16. 如果返回一个promise,会等这个promise执行完,如果成功就走下个then,如果失败或者抛出异常就走下个的失败
  17. 如果then的返回值和promise是同一个引用对象,造成循环引用,那就抛出异常
  18. 如果then的返回值是一个promise,且x同时调用resolve和reject,第一个调用优先,状态不会再次被改变
js
const PENDING = 'PENDING',
  FULFILLED = 'FULFILLED',
  REJECTED = 'REJECTED';

const resolvePromise = (promise, x, resolve, reject) => {
  if (promise === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));

  let called = false;
  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    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 {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
};

class MyPromise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    let resolve = value => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    };
    let reject = reason => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

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

  static resolve(value) {
    if (value instanceof Promise) {
      return value;
    } else {
      return new MyPromise((resolve, reject) => {
        resolve(value);
      });
    }
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason));
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : err => {
            throw err;
          };

    let promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promise;
  }
}

MyPromise.prototype.catch = function (cb) {
  return this.then(null, cb);
};

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

MyPromise.all = function (values) {
  if (!Array.isArray(values)) {
    const type = typeof values;
    return new TypeError(`TypeError: ${type} ${values} is not iterable`);
  }

  return new MyPromise((resolve, reject) => {
    let resultArr = [];
    let orderIndex = 0;
    const processResultByKey = (value, index) => {
      resultArr[index] = value;
      if (++orderIndex === values.length) {
        resolve(resultArr);
      }
    };

    for (let i = 0; i < values.length; i++) {
      let value = values[i];
      if (value && typeof value.then === 'function') {
        value.then(value => {
          processResultByKey(value, i);
        }, reject);
      } else {
        processResultByKey(value, i);
      }
    }
  });
};

MyPromise.race = function (promises) {
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i > promises.length; i++) {
      let val = promises[i];
      if (val && typeof val.then === 'function') {
        val.then(resolve, reject);
      } else {
        resolve(val);
      }
    }
  });
};

// const promise = new MyPromise((resolve, reject) => {
//   setTimeout(() => {
//     resolve('成功');
//   }, 1000);
// }).then(
//   data => console.log('resolve', data),
//   err => console.log('reject', err)
// );

const promise1 = new MyPromise((resolve, reject) => {
  reject('失败');
})
  .then()
  .then()
  .catch(err => {
    console.log('promise1 err1', err);
  })
  .catch(err => {
    console.log('promise1 err2', err);
  });

// MyPromise.deferred = function () {
//   let dfd = {};
//   dfd.promise = new MyPromise((resolve, reject) => {
//     dfd.resolve = resolve;
//     dfd.reject = reject;
//   });
//   return dfd;
// };

// module.exports = MyPromise;

export default MyPromise;

promise的方法实现

js
static resolve(data){
  if(data instanceof Promise){

  }

}

Released under the MIT License.