实现一个简单的Promise

来源:home1024 分类:JavaScript

Promise 可以理解为一个容器,保存着所有的异步操作。 将异步操作用同步的形式展现出来。 提供了统一的 API,保证各种一步操作使用统一的方法处理。

对比旧式的回调函数的写法,避免重重嵌套各种回调函数,Promise 使用链式的方式处理异步操作更简洁明了。 Promise 的状态不会受外界影响改变,有三个状态:pending、resolved 和 rejected。 一旦状态改变,就不会再变。状态改变是只有两种方式,从 pending 变为 resolved 和 pending 变为 rejected。

缺点:Promise 一旦创建,则无法取消。如果不设置回调函数,则会内部报错,不会反应到外部。当 Promise 处于 pending 状态,我们无法得知具体是在哪个阶段(是刚开始呢?还是已经快结束了?)。

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';

class MyPromise {
  static resolve(value) {
    if (value && value.then) {
      return value;
    }
    return new MyPromise((resolve) => resolve(value));
  }

  constructor(fn) {
    this.status = PENDING;
    this.value = null;
    this.reason = null;
    this.onResolvedCallback = [];
    this.onRejectedCallback = [];
    const resolve = (value) => {
      if (this.status === PENDING) {
        setTimeout(() => {
          this.status = RESOLVED;
          this.value = value;
          this.onResolvedCallback.forEach(({ fn, resolve, reject }) => {
            return resolve(fn(value));
          });
        });
      }
    };
    const reject = (e) => {
      if (this.status === PENDING) {
        setTimeout(() => {
          this.status = REJECTED;
          this.reason = e;
          this.onRejectedCallback.forEach(({ fn, resolve, reject }) => {
            return reject(fn(this.reason));
          });
        });
      }
    };
    fn(resolve, reject);
  }

  then(fn) {
    const status = this.status;
    if (PENDING === status) {
      return new MyPromise((resolve, reject) => {
        this.onResolvedCallback.push({ fn, resolve, reject });
      });
    } else if (RESOLVED === status) {
      return MyPromise.resolve(fn(this.value));
    }
  }

  catch(fn) {
    const status = this.status;
    if (PENDING === status) {
      return new MyPromise((resolve, reject) => {
        this.onRejectedCallback.push({ fn, resolve, reject });
      });
    } else if (REJECTED === status) {
      return MyPromise.resolve(fn(this.value));
    }
  }
}

MyPromise.resolve(10)
  .then((res) => {
    console.log('first called', res);
    return res * 2;
  })
  .then((res) => {
    console.log('second called', res);
  });

new MyPromise((resolve, reject) => reject('null')).catch((e) => {
  console.log('error', e);
});