来实现一个简易版的 Promise

1,781 阅读3分钟

前言

Promise 是 JavaScript 中用于异步操作非常重要的一个构造函数,那么它是怎么实现的呢?

下面用原生 JavaScript 来实现一个简易版的 Promise,用来实现以下代码。

function Promise(???){
    ???
    return ???
}

var promise = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve('hello');
  }, 3000);
});

promise.then(
  val => {
    console.log('第一次成功', val);// 输出 第一次成功 hello
  },
  val => {
    console.log('第一次失败', val);
  }
);

实现

用过 Promise 的朋友都知道,Promise 要接受一个函数 fn 并调用它,Promise 还应该有一个表示状态( pending, resolved, rejected )的属性值 status,一个 value 存储 then 回调时传的值,还有一个 then 方法。其他方法暂时不考虑了。

function Promise(fn) {
  //初始 status 为 peding
  let status = 'pending';
  //存储 then 回调值
  let value;
  //存储 then 的两个参数
  let onResolvedCallback;
  let onRejectedCallback;

  //设置 fn 的 resolve 函数
  function resolve(data) {
    //将 status 变成 fulfilled
    status = 'fulfilled';
    //存储回调函数的值
    value = data;
    toDoThen(onResolvedCallback, onRejectedCallback);
  }

  //设置 fn 的 reject 函数
  function reject(data) {
    //将 status 变成 rejected
    status = 'rejected';
    //存储回调函数的值
    value = data;
    toDoThen(onResolvedCallback, onRejectedCallback);
  }

  //设置 resolve 或 reject 时的执行体
  function toDoThen(onFulfill, onReject) {
    //如果是 fulfilled 状态,表示要执行 then 的第一个函数
    if (status === 'fulfilled') {
      onFulfill && onFulfill(value);
      status = 'pending';
    //如果是 rejected 状态,表示要执行 then 的第二个函数
    } else if (status === 'rejected') {
      onReject && onReject(value);
      status = 'pending';
    //如果状态还是 peding,表示 resolve 或 reject 还没执行,那就先把 then 的两个函数存起来,等 resolve 或 reject 的时候再调用
    } else {
      onResolvedCallback = onFulfill;
      onRejectedCallback = onReject;
    }
  }

  //将 then 得到的函数传给 toDoThen
  this.then = function(onFulfill, onReject) {
    toDoThen(onFulfill, onReject);
  };

  //执行 fn
  fn(resolve, reject);

  return this;
}

测试一波

var promise = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve('hello');
  }, 3000);
});

promise.then(
  val => {
    console.log('第一次成功', val);
  },
  val => {
    console.log('第一次失败', val);
  }
);

执行时会打印出 '第一次成功' 和 'hello'

链式 then

如果想要实现以下的代码,让第二个 then 的执行函数是根据第一个 then 返回值的布尔值来确定的。

promise
  .then(
    val => {
      console.log('第一次成功', val);
      return 'world';
    },
    val => {
      console.log('第一次失败', val);
      return false;
    }
  )
  .then(
    val => {
      console.log('第二次成功', val);
    },
    val => {
      console.log('第二次失败', val);
    }
  );

这里如果要实现链式 then,那么 then 返回的也必须是一个 Promise 对象,并根据第一个 then 的返回值来确定下一个 then 调用哪个函数。

function Promise(fn) {
  //初始 status 为 peding
  let status = 'pending';
  //存储 then 回调值
  let value;
  //存储 then 的两个参数
  let onResolvedCallback;
  let onRejectedCallback;

  //设置 fn 的 resolve 函数
  function resolve(data) {
    //将 status 变成 fulfilled
    status = 'fulfilled';
    //存储回调函数的值
    value = data;
    toDoThen(onResolvedCallback, onRejectedCallback);
  }

  //设置 fn 的 reject 函数
  function reject(data) {
    //将 status 变成 rejected
    status = 'rejected';
    //存储回调函数的值
    value = data;
    toDoThen(onResolvedCallback, onRejectedCallback);
  }

  //设置 resolve 或 reject 时的执行体
  function toDoThen(onFulfill, onReject) {
    //如果是 fulfilled 状态,表示要执行 then 的第一个函数
    if (status === 'fulfilled') {
      onFulfill && onFulfill(value);
      status = 'pending';
    //如果是 rejected 状态,表示要执行 then 的第二个函数
    } else if (status === 'rejected') {
      onReject && onReject(value);
      status = 'pending';
    //如果状态还是 peding,表示 resolve 或 reject 还没执行,那就先把 then 的两个函数存起来,等 resolve 或 reject 的时候再调用
    } else {
      onResolvedCallback = onFulfill;
      onRejectedCallback = onReject;
    }
  }

  this.then = function(onFulfill, onReject) {
    return new Promise((resolve, reject) => {
      toDoThen(
        val => {
          let result = onFulfill(val);
          //根据第一个 then 的返回值来确定下一个 then 的调用
          result ? resolve(result) : reject(result);
        },
        err => {
          let result = onReject(err);
          //根据第一个 then 的返回值来确定下一个 then 的调用
          result ? resolve(result) : reject(result);
        }
      );
    });
  };

  //执行 fn
  fn(resolve, reject);

  return this;
}

来执行一下

var promise = new Promise(function(resolve, reject) {
  setTimeout(() => {
    resolve('hello');
  }, 3000);
});

promise
  .then(
    val => {
      console.log('第一次成功', val);
      return 'world';
    },
    val => {
      console.log('第一次失败', val);
      return false;
    }
  )
  .then(
    val => {
      console.log('第二次成功', val);
    },
    val => {
      console.log('第二次失败', val);
    }
  )

程序会依次输出 '第一次成功' 'hello' 和 '第二次成功' 'world' 。

到此就实现了一个简易版的 Promise,当然,还有很多地方细节没有考虑到,实现地非常粗糙。