Skip to content

Promise

构造函数

按照Promise/A+规范+ES6实现完整的Promise

首先Promise是一个构造函数,并且需要传入一个函数作为参数(executor),这个函数会在主执行栈中同步执行。

executor函数接收两个参数:resolve和reject,这两个参数都是函数。

js
class MyPromise {
  constructor(executor) {
    const resolve = () => {}

    const reject = () => {}

    executor(resolve, reject)
  }
}

实现要求

Promise 状态

文档原文A+规范

2.1.1. When pending, a promise:
  2.1.1.1. may transition to either the fulfilled or rejected state.
2.1.2. When fulfilled, a promise:
  2.1.2.1. must not transition to any other state.
  2.1.2.2. must have a value, which must not change.
2.1.3 When rejected, a promise:
  2.1.3.1. must not transition to any other state.
  2.1.3.2. must have a reason, which must not change.
Here, “must not change” means immutable identity (i.e. ===),but does not imply deep immutability.

img

注意最后一句话的意思,意思指的是不可变的值仅代表引用地址不变,而不是深层次的不可变状态,用原生的promise举例子来说

js
const obj = {
  a: 1,
  b: {
    c: 2
  }
}

const promise = new Promise((resolve, reject) => {
  resolve(obj.b)

  obj.b.c = 3
})

这样是被允许的

用我自己理解的话来说:

  1. promise存在三个状态:pendingfulfilledrejected
  2. 状态之间只能改变一次
  3. 必须传递参数resolve(value)reject(reason)
js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECRD = 'rejected'
class MyPromise {
  status = PENDING
  value = undefined
  reason = undefined
  constructor(executor) {
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = value
      }
    }

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECRD
        this.reason = reason
      }
    }

    executor(resolve, reject)
  }
}

then 方法

基础实现

文档原文A+规范

A promise must provide a then method to access its current or eventual value or reason.

A promise’s then method accepts two arguments:

promise.then(onFulfilled, onRejected)

2.2.1. Both onFulfilled and onRejected are optional arguments:
  2.2.1.1. If onFulfilled is not a function, it must be ignored.
  2.2.1.2. If onRejected is not a function, it must be ignored.
2.2.2. If onFulfilled is a function:
  2.2.2.1. it must be called after promise is fulfilled, with promise’s value as its first argument.
  2.2.2.2. it must not be called before promise is fulfilled.
  2.2.2.3. it must not be called more than once.
2.2.3. If onRejected is a function,
  2.2.3.1. it must be called after promise is rejected, with promise’s reason as its first argument.
  2.2.3.2. it must not be called before promise is rejected.
  2.2.3.3. it must not be called more than once.

img

按照自己理解的意思:

  1. promise必须提供then方法,来访问当前的或最终的value or reason
  2. then方法接受两个参数:onFulfilledonRejected
  3. onFulfilledonRejected 都是可选参数的,只判断是函数的情况下才调用
  4. onFulfilledonRejected 不能被多次调用(状态只会更改一次,这里不需要特别关注)
js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECRD = 'rejected'
class MyPromise {
  status = PENDING
  value = undefined
  reason = undefined
  constructor(executor) {
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = value
      }
    }

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECRD
        this.reason = reason
      }
    }

    executor(resolve, reject)
  }

  then(onFulfilled, onRejected) {
    // 后面会优化这部分内容
    if (isFunction(onFulfilled)) {
      if (this.status === FULFILLED) {
        onFulfilled(this.value)
      }
    }

    if (isFunction(onRejected)) {
      if (this.status === REJECRD) {
        onRejected(this.reason)
      }
    }
  }
}

异步处理

文档原文A+规范

2.2.4. onFulfilled or onRejected must not be called until the execution context stack contains only platform code.
2.2.5. onFulfilled and onRejected must be called as functions (i.e. with no this value).
2.2.6. then may be called multiple times on the same promise.
  2.2.6.1. If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then.
  2.2.6.2. If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then.

img

在原生Promise中,如果executor执行了异步代码,并在异步中resolvepromise会等待这个异步完成后才会执行,也就是会等待这个异步

js
const promise = new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve("success")
  },2000)
})

promise.then(res=>{
  console.log(res) // 会在2000ms后输出 "success"
})
  1. 2.2.4 这句话的意思指的是,Promise的回调函数必须等当前所有同步代码都执行完后才会执行。也就是实现异步等待的过程,可以通过宏任务setTimeout、setImmediate或者微任务机制MutationObserver、process.nextTick、queueMicrotask实现。
  2. onFulfilledonRejected 必须是作为函数调用,如果不是函数,可以参数归一成函数,将当前的值传递
  3. then可能会在同一个promise上多次调用,这时要处理多个调用可能是异步的情况,处理这种异步队列的方式,借鉴发布订阅的思想
js
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECRD = 'rejected'
class MyPromise {
  status = PENDING
  value = undefined
  reason = undefined

  // 处理多次调用,以及异步时状态还在pending的情况
  onFulfilledCallbacks = []
  onRejectedCallbacks = []

  constructor(executor) {
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED
        this.value = value

        /**
         * 发布过程
         * 为什么要在这里发布呢?因为当executor中出现了异步任务,但无论是什么异步,都需要执行resolve
         * 当执行了resolve,也就说明了在当前函数中可以获取到最新的status以及value
         */
        this.onFulfilledCallbacks.forEach((fn) => fn())
      }
    }

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECRD
        this.reason = reason

        // 发布过程
        this.onRejectedCallbacks.forEach((fn) => fn())
      }
    }

    executor(resolve, reject)
  }

  then(onFulfilled, onRejected) {
    // 参数归一处理
    onFulfilled = isFunction(onFulfilled) ? onFulfilled : (value) => value
    onRejected = isFunction(onRejected)
      ? onRejected
      : (reason) => {
          // 这里因为要被捕获,所以不能直接返回reason,而是要throw
          throw reason
        }

    if (this.status === FULFILLED) {
      onFulfilled(this.value)
    }

    if (this.status === REJECRD) {
      onRejected(this.reason)
    }

    // 处理异步的问题,在等待异步的过程中,状态还处于pending的阶段,
    // 要将当前的函数执行收集,并在确定状态后释放
    if (this.status === PENDING) {
      // 订阅过程
      this.onFulfilledCallbacks.push(() => {
        onFulfilled(this.value)
      })

      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason)
      })
    }
  }
}

链式调用

then的运行原则细节:

  1. 走onFulfilled的条件
    1. then 返回了一个普通的JavaScript值
    2. then 返回了新的promise成功态的结果
  2. 走onRejected的条件
    1. then 返回了一个新的promise失败态的结果
    2. then 抛出了异常
  3. catch实际就是一个then,也遵循then的运行原则,catch = (onRejected)=> this.then(null, onRejected);
  4. promise的链式调用,实际上是在then里返回了一个promise
js
let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
  }, 2000);
});

promise
  .then((value) => {
    // 返回普通值
    return value;
  })
  .then((value) => {
    // 返回promise
    return new Promise((resolve, reject) => {
      resolve(value);
    });
  })
  .then((value) => {
    // onFulfilled
    console.log("fulfilled", value);
    return new Promise((resolve, reject) => {
      reject("error");
    });
  })
  .then(
    () => {},
    (reason) => {
      // onRejected
      console.log("rejected", reason);
      throw new Error("error");
    }
  )
  .then(
    () => {},
    (reason) => {
      // 1. reason: 会找最近的onRejected或catch,如果有任一处理,不再往后冒泡
      console.log("last reason", reason);
    }
  )
  .catch((error) => {
    console.log("catch error", error);
    // 2. reason: 会找最近的onRejected或catch,如果有任一处理,不再往后冒泡
    // 在catch可以继续链式调用,返回普通值或promise
    // 其实catch就是一个没有onFulfilled的then,catch =(cb)=> then(null,cb)
    return "catch";
  })
  .then((value) => {
    console.log("catch + then", value);
  });

文档原文A+规范

2.2.7then must return a promise [3.3].
promise2 = promise1.then(onFulfilled, onRejected);
  2.2.7.1. If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
  2.2.7.2. If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
  2.2.7.3. If onFulfilled is not a function and promise1 is fulfilled, promise2 must be fulfilled with the same value as promise1.
  2.2.7.4. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason as promise1.

img

js
class MyPromise {
  status = PENDING;
  value = undefined;
  reason = undefined;

  onFulfilledCallbacks = [];
  onRejectedCallbacks = [];

  constructor(executor) {
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;

        this.onFulfilledCallbacks.forEach((fn) => fn());
      }
    };

    const reject = (reason) => {
      if (this.status === REJECTED) {
        this.status = REJECTED;
        this.reason = reason;

        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };

    executor(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    // 2.2.7 then中必须要返回一个promise
    const prosmise2 = new MyPromise((resolve, reject) => {
      // 2.2.7.3
      // 2.2.7.4
      // 如果onFulfilled和onRejected不是一个函数,按照原值处理
      onFulfilled = isFunction(onFulfilled) ? onFulfilled : (value) => value;
      onRejected = isFunction(onRejected)
        ? onRejected
        : (reason) => {
            throw reason;
          };

      const handleCallback = (status, valueOrReason, needAsync = true) => {
        const originalFn = () => {
          try {
            let x = undefined;
            // 2.2.7.1 onFulfilled和onRejected会返回一个x
            if (status === FULFILLED) {
              x = onFulfilled(valueOrReason);
            } else {
              x = onRejected(valueOrReason);
            }
            // x可能是是普通值 or promise,新返回的promise的结果需要依赖resolve去解决
            // 所以需要一个程序去判断这部分的逻辑
            // prosmise2此时还未初始化完成,需要异步拿到变量,
            // 也就是文档2.2.4所描述的确保 onFulfilled 和 onRejected 的执行是异步的
            // TODO 接下来就是要处理x是一个thenable的逻辑
            resolvePromise(prosmise2, x, resolve, reject);
          } catch (error) {
            // 2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 TypeError 必须拒绝完成,并返回拒绝原因 e
            reject(error);
          }
        };
        needAsync ? runMicrotasks(originalFn) : originalFn();
      };

      if (this.status === FULFILLED) {
        handleCallback(FULFILLED, this.value);
      }

      if (this.status === REJECTED) {
        handleCallback(REJECTED, this.reason);
      }

      if (this.status === PENDING) {
        // 这里为什么不需要异步处理呢,因为此时的状态是pending,这里不会执行也不会拿promise2,
        // 等到resolve或reject才会拿promise2的值,那个时候promise2已经实例化完成
        this.onFulfilledCallbacks.push(() => handleCallback(FULFILLED, this.value, false));
        this.onRejectedCallbacks.push(() => handleCallback(REJECTED, this.reason, false));
      }
    });

    return prosmise2;
  }
}

文档原文A+规范

2.3.1. If promise and x refer to the same object, reject promise with a TypeError as the reason.
2.3.2. If x is a promise, adopt its state [3.4]:
  2.3.2.1. If x is pending, promise must remain pending until x is fulfilled or rejected.
  2.3.2.2. If/when x is fulfilled, fulfill promise with the same value.
  2.3.2.3. If/when x is rejected, reject promise with the same reason.
2.3.3. Otherwise, if x is an object or function,
  2.3.3.1. Let then be x.then. [3.5]
  2.3.3.2. If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
  2.3.3.3. If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise, where:
    2.3.3.3.1. If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
    2.3.3.3.2. If/when rejectPromise is called with a reason r, reject promise with r.
    2.3.3.3.3. If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
    2.3.3.3.4. If calling then throws an exception e,
      2.3.3.3.4.1. If resolvePromise or rejectPromise have been called, ignore it.
      2.3.3.3.4.2. Otherwise, reject promise with e as the reason.
  2.3.3.4. If then is not a function, fulfill promise with x.
2.3.4. If x is not an object or function, fulfill promise with x.

img

js
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

function isObject(value) {
  return value !== null && typeof value === "object";
}

function isFunction(value) {
  return typeof value === "function";
}

/**
 * The Promise Resolution Procedure
 * promise then的解析过程
 */
function resolvePromise(promise2, x, resolve, reject) {
  // 2.3.1 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
  if (promise2 === x) {
    reject(new TypeError("Chaining cycle detected for promise #<MyPromise>"));
  }
  // 2.3.3 如果x是一个Object或Function
  if (isFunction(x) || isObject(x)) {
    // 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
    // 也就是做个flag标记,避免多次调用
    let called = false;
    try {
      // 2.3.3.1 让then指向x.then
      const then = x.then;

      // 2.3.3.3 如果then是一个函数
      if (isFunction(then)) {
        // 2.3.3.3 以x作为this调用then,传递两个函数
        then.call(
          x,
          (y) => {
            if (called) {
              return;
            }
            called = true;

            // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则递归运行 [[Resolve]](promise, y)
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) {
              return;
            }
            called = true;

            // 2.3.3.3.2 如果 rejectPromise 以拒绝原因 r 为参数被调用,则以拒绝原因 r 拒绝 promise
            reject(r);
          }
        );
      } else {
        // 2.3.4 如果 then 不是函数,以 x 为参数完成 promise
        resolve(x);
      }
    } catch (error) {
      // 在抛出错误时,也不能重复调用
      if (called) return;
      called = true;

      // 2.3.3.2 如果取x.then值时抛出错误error,则以error为据因拒绝执行promise
      reject(error);
    }
  } else {
    // 2.3.4 如果不是object或function,以x为值承诺这个promise
    resolve(x);
  }
}

/**
 * runMicrotasks - 执行微任务函数
 * 按照优先级依次使用 queueMicrotask、process.nextTick、MutationObserver、setTimeout
 * 确保任务以微任务的形式执行,如果环境不支持微任务则降级使用宏任务
 */
function runMicrotasks(fn) {
  if (typeof queueMicrotask === "function") {
    queueMicrotask(fn);
  } else if (typeof process === "object" && typeof process.nextTick === "function") {
    process.nextTick(fn);
  } else if (typeof MutationObserver === "function") {
    const observer = new MutationObserver(fn);
    const textNode = document.createTextNode("1");
    observer.observe(textNode, {
      characterData: true,
    });
    textNode.textContent = "2";
  } else {
    setTimeout(fn, 0);
  }
}

class MyPromise {
  state = PENDING;
  value = undefined;
  reason = undefined;

  onFulfilledCallbacks = [];
  onRejectedCallbacks = [];

  constructor(executor) {
    const resolve = (value) => {
      if (this.state === PENDING) {
        this.state = FULFILLED;
        this.value = value;

        this.onFulfilledCallbacks.forEach((fn) => fn(value));
      }
    };

    const reject = (reason) => {
      if (this.state === PENDING) {
        this.state = REJECTED;
        this.reason = reason;

        this.onRejectedCallbacks.forEach((fn) => fn(reason));
      }
    };

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

  then(onFulfilled, onRejected) {
    // 2.2.7.3
    // 2.2.7.4
    // 如果onFulfilled和onRejected不是一个函数,按照原值处理
    const onFulfilledNow = isFunction(onFulfilled) ? onFulfilled : (value) => value;
    const onRejectedNow = isFunction(onRejected)
      ? onRejected
      : (reason) => {
          throw reason;
        };

    // 2.2.7 then中必须要返回一个promise
    const promise2 = new MyPromise((resolve, reject) => {
      const handleResolve = (value) => {
        try {
          // 2.2.7.1 onFulfilled和onRejected会返回一个x
          const x = onFulfilledNow(value);

          // x可能是是普通值 or promise,新返回的promise的结果需要依赖resolve去解决
          // 所以需要一个程序去判断这部分的逻辑
          // prosmise2此时还未初始化完成,需要异步拿到变量,
          // 也就是文档2.2.4所描述的确保 onFulfilled 和 onRejected 的执行是异步的
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          // 2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 TypeError 必须拒绝完成,并返回拒绝原因 e
          reject(error);
        }
      };

      const handleReject = (reason) => {
        try {
          const x = onRejectedNow(reason);
          resolvePromise(promise2, x, resolve, reject);
        } catch (error) {
          reject(error);
        }
      };

      if (this.state === FULFILLED) {
        runMicrotasks(() => handleResolve(this.value));
      }

      if (this.state === REJECTED) {
        runMicrotasks(() => handleReject(this.reason));
      }

      if (this.state === PENDING) {
        // 这里为什么不需要异步处理呢,因为此时的状态是pending,这里不会执行也不会拿promise2,
        // 等到resolve或reject才会拿promise2的值,那个时候promise2已经实例化完成
        this.onFulfilledCallbacks.push((value) => runMicrotasks(() => handleResolve(value)));
        this.onRejectedCallbacks.push((reason) => runMicrotasks(() => handleReject(reason)));
      }
    });

    return promise2;
  }
}

通过 promises-aplus-testes 的所有测试用例

img

catch

MDN文档

它是 then(undefined, onRejected) 的快捷方式。

js
class MyPromise{
  // ...
  catch(onRejected){
    this.then(null, onRejected)
  }
}

finally

静态方法

all

allSettled

any

race

reject

resolve

Promise.resolve(value) 是一个静态方法

  1. Promise.resolve() 返回一个 Promise
  2. 如果value.constructor === Promise,直接返回value
  3. Promise.resolve() 本质上是 new Promise((resolve) => resolve(value)) .
js
class MyPromise{
  //...
  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    }
    return new MyPromise((resolve) => resolve(value));
  }
}

try

withResolvers

上次更新于:

如有转载或 CV 的请标注本站原文地址