手写Promise
简易版本
初始代码
// Promise 的三个状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(executer) {
  const that = this; // 缓存that是因为代码可能会异步执行,用于获取正确的 this 对象
  that.state = PENDING; // 初始值是pending
  that.value = null; // 保存resolve或者reject传入的值
  that.resolvedCallbacks = [];
  that.rejectedCallbacks = [];
}
- resolvedCallbacks 和 rejectedCallbacks 用于保存 then 中的回调,因为当执行完 Promise 时状态可能还是等待中,这时候应该把 then 中的回调保存起来用于状态改变时使用
resolve、reject 函数
写在 Promise 内部
function resolve(val) {
  if (that.state === PENDING) {
    that.state = RESOLVED;
    that.value = val;
    that.resolvedCallbacks.map(cb => cb(that.value));
  }
}
function reject(reason) {
  if ((that.state = PENDING)) {
    that.state = REJECTED;
    that.value = reason;
    that.rejectedCallbacks(cb => cb(that.value));
  }
}
状态只能从 pending 变成 resolved/rejected
- 首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态
- 将当前状态更改为对应状态,并且将传入的值赋值给 value
- 遍历回调数组并执行
执行传入的函数
完成以上两个函数以后,我们就该实现如何执行 Promise 中传入的函数了
try {
  fn(resolve, reject);
} catch (e) {
  reject(e);
}
- 要注意的是,可能执行函数过程中会遇到错误,需要捕获错误并且执行 reject 函数
then 函数的实现
MyPromise.prototype.then = function(onFulfilled, onRejected) {
  const that = this;
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : e => {
          throw e;
        };
  if (that.state === PENDING) {
    that.resolvedCallbacks.push(onFulfilled);
    that.rejectedCallbacks.push(onRejected);
  }
  if (that.state === RESOLVED) {
    onFulfilled(that.value);
  }
  if (that.state === REJECTED) {
    onRejected(that.value);
  }
};
- 判断传入的是否是函数,如果不是则透传之前的值
- 当状态不是等待态时,就去执行相对应的函数。如果状态是等待态的话,就往回调函数中 push 函数
实现一个符合 Promise/A+ 规范的 Promise
改造一下 resolve 和 reject 函数
function resolve(value) {
  if (value instanceof MyPromise) {
    return value.then(resolve, reject);
  }
  setTimeout(() => {
    if (that.state === PENDING) {
      that.state = RESOLVED;
      that.value = value;
      that.resolvedCallbacks.forEach(cb => cb(that.value));
    }
  }, 0);
}
function reject(reason) {
  setTimeout(() => {
    if (that.state === PENDING) {
      that.state = REJECTED;
      that.value = reason;
      that.rejectedCallbacks.forEach(cb => cb(that.value));
    }
  }, 0);
}
- Promise 是异步执行回调的
改造 then 函数
首先我们需要新增一个变量 promise2,因为每个 then 函数都需要返回一个新的 Promise 对象,该变量用于保存新的返回对象
MyPromise.prototype.then = function(onFulfilled, onRejected) {
  const that = this;
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : e => {
          throw e;
        };
  const p2 = new MyPromise((resolve, reject) => {
    if (that.state === PENDING) {
      that.resolvedCallbacks.push(() => {
        try {
          const x = onFulfilled(that.value);
          resolutionProcedure(p2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
      that.rejectedCallbacks.push(() => {
        try {
          const x = onRejected(that.value);
          resolutionProcedure(p2, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    }
  });
  if (that.state === RESOLVED) {
    setTimeout(() => {
      try {
        const x = onFulfilled(that.value);
        resolutionProcedure(p2, x, resolve, reject);
      } catch (e) {
        reject(e);
      }
    }, 0);
  }
  if (that.state === REJECTED) {
    setTimeout(() => {
      try {
        const x = onRejected(that.value);
        resolutionProcedure(p2, x, resolve, reject);
      } catch (e) {
        reject(e);
      }
    }, 0);
  }
};
- 首先我们返回了一个新的 Promise 对象,并在 Promise 中传入了一个函数
- 函数的基本逻辑还是和之前一样,往回调数组中 push 函数
- 同样,在执行函数的过程中可能会遇到错误,所以使用了 try...catch 包裹
- 规范规定,执行 onFulfilled 或者 onRejected 函数时会返回一个 x,并且执行 Promise 解决过程,这是为了不同的 Promise 都可以兼容使用(then 方法传入的函数的返回值也可能是 Promise)
promise 的解决过程
function resolutionProcedure(p2, x, resolve, reject) {
  // 首先规范规定了 x 不能与 promise2 相等,这样会发生循环引用的问题
  if (p2 === x) {
    throw reject(new TypeError("循环引用"));
  }
  // 这里的代码是完全按照规范实现的。如果 x 为 Promise 的话,需要判断以下几个情况:
  // 1. 如果 x 处于等待态,Promise 需保持为等待态直至 x 被执行或拒绝
  // 2. 如果 x 处于其他状态,则用相同的值处理 Promise
  if (x instanceof MyPromise) {
    // 先判断x的状态
    if (x.state === PENDING) {
      x.then(y => {
        // x状态还未改变,返回的下一个promise的resove的接收的值y不确定,对其递归处理
        resolutionProcedure(p2, y, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
    return;
  }
  let called = false;
  if (x !== null && (typeof x === "object" || typeof x === "function")) {
    try {
      const then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            resolutionProcedure(p2, y, resolve, reject);
          },
          e => {
            if (called) return;
            called = true;
            reject(e);
          }
        );
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}
- 首先创建一个变量 called 用于判断是否已经调用过函数
- 然后判断 x 是否为对象或者函数,如果都不是的话,将 x 传入 resolve 中
- 如果 x 是对象或者函数的话,先把 x.then 赋值给 then,然后判断 then 的类型,如果不是函数类型的话,就将 x 传入 resolve 中
- 如果 then 是函数类型的话,就将 x 作为函数的作用域 this 调用之,并且传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise,两个回调函数都需要判断是否已经执行过函数,然后进行相应的逻辑
加 called 的目的
// 比如下面这种情况, 就没要在执行onFulfilled(3);
let p = new MyPromise(resolve => {
  resolve(1);
})
  .then(v => {
    return {
      then: function(onFulfilled) {
        onFulfilled(2);
        onFulfilled(3);
      }
    };
  })
  .then(v => {
    console.log(v);
  });
Promise 规范
术语
- ‘promise’ 是一个有符合此标准的 then 方法的 object 或 function
- ‘thenable’ 是 then 方法定义的 object 或 function
- ‘value’ 是一个 JavaScript 合法值(包括 undefined,thenable,promise)- resolve 的 value
- ‘exception’ 是一个 throw 语句抛出错误的值
- ‘reason’ 是一个表明 promise 失败的原因的值
要求
状态
- pending
- resolved
- rejected
只能由 pending -> resolved / pending -> rejected
resolved 状态
- 不能在改变为其他状态
- 必须要有一个 value
rejected 状态
- 不能再改变为其他状态
- 必须要有一个 reason
then 方法
一个 promise 必须提供一个 then 方法,用来获取当前或最终的 value 或 reason
一个 promise 的 then 方法接受两个参数:
promise.then(onFulfilled, onRejected)
- onFulfilled 和 onRejected 都是可选的,如果没有提供或者提供的不是函数则将值和 reason 透传 
- 如果 onFulfilled 是一个函数 
- 他会在 promise 的状态变为 resolved 后调用,并且接收到一个 value
- 只会被调用一次
- 如果 onRejected 是一个函数
- 他会在 promise 的状态变为 rejected 之后调用,并且接收到一个 reason
- 只会被调用一次
- onFulfilled 或 onRejected 都是异步调用,也就是说等同步代码执行完毕之后调用 
- promise 的 then 可以链式调用多次 
- 如果或当 promise 转态是 resolved 时,所有的 onFulfilled 回调回以他们注册时的顺序依次执行
- 如果或当 promise 转态是 rejected 时,所有的 onRejected 回调回以他们注册时的顺序依次执行
- then 方法返回一个 promise promise2 = promise1.then(onFulfilled, onRejected);
- 如果 onFulfilled 或 onRejected 返回的是一个 x,那么它会以 [[Resolve]](promise2, x)处理解析
- 如果 onFulfilled 或 onRejected 里抛出了一个异常,那么 promise2 必须捕获这个错误(接受一个 reason 参数)
if (that.state === RESOLVED) {
  setTimeout(() => {
    try {
      const x = onFulfilled(that.value);
      resolutionProcedure(p2, x, resolve, reject);
    } catch (e) {
      reject(e);
    }
  }, 0);
}
if (that.state === REJECTED) {
  setTimeout(() => {
    try {
      const x = onRejected(that.value);
      resolutionProcedure(p2, x, resolve, reject);
    } catch (e) {
      reject(e);
    }
  }, 0);
}
- 如果 onFulfilled 不是一个函数,并且 promise1 状态是 resolved, promise2 一定会接受到与 promse1 一样的值 value
- 如果 onRejected 不是一个函数,并且 promise1 状态是 rejected,promise2 一定会接受到与 promise1 一样的值 reason
其实也就是值透传
Promise 的处理程序
promise 处理程序是一个表现形式为 [[Resolve]](promise, x) 的抽象处理操作。如果 x 是 thenable 类型,它会尝试生成一个 promise 处理 x,否则它将直接 resolve x
[[Resolve]](promise, x) 的执行表现形式如下步骤:
- 如果返回的 promise1 和 x 是指向同一个引用(循环引用),则抛出错误
- 如果 x 是一个 promise 实例,则采用它的状态:
- 如果 x 是 pending 状态,那么保留它(递归执行这个 promise 处理程序),直到 pending 状态转为 resolved 或 rejected 状态
- 如果或当 x 状态是 resolved,resolve 它,并且传入和 promise1 一样的值 value
- 如果或当 x 状态是 rejected,reject 它,并且传入和 promise1 一样的值 reason
- 如果 x 是个对象或函数类型
- 把 x.then 赋值给 then 变量
- 如果捕获(try,catch)到 x.then 抛出的错误的话,需要 reject 这个 promise
- 如果 then 是函数类型,那个用 x 调用它(将 then 的 this 指向 x),第一个参数传 resolvePromise ,第二个参数传 rejectPromise- 如果或当 resolvePromise 被调用并接受一个参数 y 时,执行 [[Resolve]](promise, y)
- 如果或当 rejectPromise 被调用并接受一个参数 r 时,执行 reject(r)
- 如果 resolvePromise 和 rejectPromise 已经被调用或以相同的参数多次调用的话吗,优先第一次的调用,并且之后的调用全部被忽略(避免多次调用)
- 如果 then 执行过程中抛出了异常- 如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略异常
- 否则,则 reject 这个异常
 
 
- 如果或当 resolvePromise 被调用并接受一个参数 y 时,执行 
- 如果 then 不是函数类型,直接 resolve x(resolve(x))
- 如果 x 即不是函数类型也不是对象类型,直接 resolve x(resolve(x))
备注
- onFulfilled 和 onRejected 都在下一轮的事件循环中(一个新的栈)被异步调用。可以用宏任务,例如:setTimeout,setImmediate 或者微任务,例如:MutationObsever 或 process.nextTick 实现。 由于 promise 的实现被当做平台代码,所以它本身可能包含一个任务队列或 “trampoline” 的处理程序