Promise的鏈式調用原理

關於 Promise 的基礎概念和使用方法就很少講了,咱們來聊一聊它的實現原理promise

請你們配合最下方的 Promise/A+ 規範實現代碼來閱讀瀏覽器

首先明確一個概念:決議,就是指將 Promise 的狀態從 pending 變爲 fulfilled/rejected異步

其次 Promise/A+ 規範實現代碼中的 setTimeout 能夠理解將代碼添加到平臺的微任務隊列中異步執行函數

結合代碼描述 Promise 鏈式調用的整個流程

  1. new Promise(excutor = (resolve, reject) => {}) 後 執行 excutor , 返回一個 Promise 對象 objui

    try {
        excutor(resolve, reject);
      } catch (e) {
        reject(e);
      }
    複製代碼
  2. 執行 obj.then(onFulfilled, onRejected)this

    首先判斷 onFulfilled 和 onRejected 是不是個函數,若是不是的話就將 onFulfilled 改寫成 value => value ,將 onRejected 改寫成 reason => { throw reason }spa

    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
     onRejected =
       typeof onRejected === "function"
         ? onRejected
         : reason => {
             throw reason;
           };
    複製代碼
  3. obj.then 返回一個 newPromise,它的 excutor 方法與 obj 此時的狀態有關prototype

    • 若是 obj 爲 pending 狀態code

      return newPromise = new Promise((resolve, reject) => {
        that.onFulfilledCallbacks.push(value => {
          try {
            let x = onFulfilled(value);
            resolvePromise(newPromise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
        that.onRejectedCallbacks.push(reason => {
          try {
            let x = onRejected(reason);
            resolvePromise(newPromise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      })
      複製代碼

      then 方法在 obj 處於 pending 狀態時 幹了這麼幾件事對象

      1. 建立一個新的 Promise 對象 newPromise 並返回

      2. 執行 newPromise 的 excutor,向 obj 的兩個回調隊列中添加回調

      3. 向 obj.onFulfilledCallbacks 添加一個成功回調:傳入 obj.value 執行 onFulfilled(value) 而後執行 resolvePromise,obj.value 會在 resolve(value) 執行的時候被賦值

      4. 向 obj.onRejectedCallbacks 添加一個失敗回調:傳入 obj.reason 執行 onRejected(reason) 而後執行 resolvePromise,obj.value 會在 reject(reason) 執行的時候被賦值

    • 若是 obj 已經爲 fulfilled 狀態

      return newPromise = new Promise((resolve, reject) => {
        setTimeout(() => {
          try {
            let x = onFulfilled(that.value);
            resolvePromise(newPromise, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        });
      })
      複製代碼

      咱們一樣返回了一個新的 Promise 對象,它的 excutor 函數會將 onFulfilled 和 resolvePromise 添加進微任務隊列執行,也就是說若是 obj 在已經變爲 fulfilled 後再執行 then 方法,就會在同步任務執行完畢後當即執行 then 中傳入的 onFulfilled (這個 then 方法中的 onFulfilled 不必再加入到回調隊列中,由於它等不到 obj 執行 resolve 的那一天了,Promise 的狀態一經決議就不會改變

    • 若是 obj 已經爲 rejected 狀態,所作處理基本與 fulfilled 狀態下相同

  4. 下面咱們主要分析一下 pending 狀態,若是 obj 在 pending 狀態下執行了 then ,咱們就等待 excutor 中的 resolve 或者 reject 被執行

    • resolve 執行

      接收一個參數 value , 若是 value 也是個 Promise 對象,那麼返回 value.then(reslove, reject)

      // 這裏是爲了保證一個 Promise 決議時的 value 絕對不會是另外一個 Promise
      // 好比 promise1 = new Promise(resolve => resolve(promise2 = new Promise())) 的狀況
      // 此時 promise1 決議的時候 value 爲 promise2 ,那麼就不能繼續決議 promise1 了
      // 應該執行 promise2.then(resolve, reject),等待 promise2 的決議而後再決議 promise1
      
      if (value instanceof Promise) {
        return value.then(resolve, reject);
      }
      複製代碼

      異步執行接下來的步驟(此處的異步即放置在瀏覽器的微任務隊列中):

      若是 obj.status === 'pending',改狀態爲 'fulfilled',賦值 obj.value,執行所有 onFulfilledCallbacks 回調,這個時候就會調用咱們在 then 中添加的 onFulfilled 函數

    • reject 執行

      接收一個參數 reason,異步執行接下來的步驟:

      若是 obj.status === 'pending',改狀態爲 'fulfilled',賦值 obj.reason,執行所有 onRejectedCallbacks 回調,這個時候就會調用咱們在 then 中添加的 onRejected 函數

    • 關於異步執行

      咱們說 Promise.then 是異步的,其實執行 then 方法不是異步的。可是 onFulfilled 和 onRejected 確定是異步執行的,由於 resolve 和 reject 清空回調的時候是異步執行的

  5. 執行 onFulfilled(value) 或者 onRejected(reason) 將返回值賦值給變量 x

    let x = onFulfilled(value);
    複製代碼
  6. 執行 resolvePromise(promise2, x, resolve, reject),根據 x 對 promise2 進行決議

    resolvePromise(newPromise, x, resolve, reject);
    複製代碼

    注意這裏的 promise2 是 then 方法返回的 Promise 對象,resolve 和 reject 是 promise2 的 excutor 中的 resolve 和 reject,也就是說 resolvePromise 的執行過程當中會對 promise2 進行決議

    咱們仔細分析一下 resolvePromise 都作了什麼

    首先這個參數 x 就是 onFulfilled(value) 的返回值,若是 onFulfilled 不是個函數,那它就會被改寫成 v => v,也就是 x = value

    接下來就是判斷 x 究竟是什麼

    • 若是 x === promise2,報循環引用的錯誤

    • 若是 x 是一個 Promise 對象且狀態爲 pending,那麼就執行 x.then 將 onFulfilled 加入 x.onFulfilledCallbacks 回調並等待 x 決議, x 決議後執行回調中的 onFulfilled(x.value) 也就是 x.value => resolvePromise(promise2, x.value, resolve, reject),而上文提到過此時的 x.value 毫不多是一個 Promise 了

      x.then(
        y => {
          resolvePromise(promise2, y, resolve, reject);
        },
        reason => {
          reject(reason);
        }
      );
      複製代碼
    • 若是 x 是一個 thenable 對象,處理方式同 pending 狀態的 Promise 對象,這裏使用一個 called 鎖來避免 thenable.then 屢次調用傳入的 onFulfilled/onRejected

    • 若是 x 是一個 Promise 對象且狀態不爲 pending,因此 x.value 或者 x.reason 必會存在一個,此時執行 x.then(resolve, reject),直接決議 promise2 ,即執行 resolve(x.value) 或者 reject(x.reason)

    • 若是 x 是一個普通的函數/對象,或者其餘數據類型,則 resolve(x)

從新梳理一下鏈式調用的流程

  1. promise1 = new Promise(excutor = (resolve, reject) => { ... }) 中的 excutor 是當即執行的,但最後執行 resolve 多是在異步操做中

  2. promise1.then 會給 promise1 添加回調,而後返回一個新的 promise2,這個新的 promise2 的決議依靠以前回調中的 resolvePromise 方法

  3. promise1 決議後會執行回調,首先執行 then 中傳入的 onFulfilled(promise1.value),賦值給變量 x,再執行 resolvePromise(promise2, x, promise2Resolve, promise2Reject)

  4. 若是 x 是個已決議的 Promise 或者普通的數據類型,那麼就能夠 promise2Resolve(x) 決議 promise2

  5. 若是 x 是個 pending 狀態的 promise 或者 thenable 對象,那麼執行 x.then ,將 resolvePromise 放入 x 的成功回調隊列,等待 x 決議後將 x.value 成功賦值,而後執行 resolvePromise(promise2, x.value, promise2Resolve, promise2Reject)

  6. 在此期間若是執行了 promise2.then 就新建一個 promise3 並返回 ,將新傳入的 onFulfilled(promise2.value) 和針對 promise3 的 resolvePromise 傳入 promise2 的成功回調隊列中,等待 promise2 的決議

  7. promise3.then 同上,就此實現了鏈式調用

鏈式調用的前後順序

promise1 決議後纔會決議 promise2 ,由於 promise2 的決議方法要在 promise1 的成功回調裏執行

鏈式調用的透傳

若是 promise1.then 中傳入的的 onFulfilled 不是個函數,那麼 onFulfilled 會在 then 中被改寫成 value => value,這樣就能夠將 promise1.value 傳遞給 promise2 的 resolvePromise 幫助它決議。若是剛好 value 不是一個 pending 狀態的 Promise 或者 thenable 對象,那麼 promise2 會直接決議,而後 promise1.value 會被賦值給 promise2.value 進而傳遞給 promise3,這就是所謂的透傳

思考

若是 promise2.then 的 onFulfilled 反回了一個新的 PromiseA,那麼 promise2 將直接取這個 PromiseA 的狀態和值爲己用,這發生在 resolvePromise 中;

若是 PromiseA 的 resolve 時傳入的 value 是一個新的 PromiseB,那麼 PromiseA 也會直接取這個 PromiseB 的狀態和值爲己用,這發生在 resolve 中;

也就是說,咱們能夠經過在 onFulfilled 時返回一個 newPromise,或者 resolve 時傳入一個 newPromise,再執行 newPromise.then(resolve, reject) 來實現一個 Promise 決議的掛起(後置),直至 newPromise 決議。

Promise/A+ 規範實現

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

function Promise(excutor) {
  let that = this; 
  that.status = PENDING;
  that.value = undefined;
  that.reason = undefined;
  that.onFulfilledCallbacks = [];
  that.onRejectedCallbacks = [];

  function resolve(value) {
    
    if (value instanceof Promise) {
      return value.then(resolve, reject);
    }

    setTimeout(() => {
      if (that.status === PENDING) {
        that.status = FULFILLED;
        that.value = value;
        that.onFulfilledCallbacks.forEach(cb => cb(that.value));
      }
    });
  }

  function reject(reason) {
    setTimeout(() => {
      if (that.status === PENDING) {
        that.status = REJECTED;
        that.reason = reason;
        that.onRejectedCallbacks.forEach(cb => cb(that.reason));
      }
    });
  }

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

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError("Chaining cycle detected for promise!"));
  }

  let called = false;
  if (x instanceof Promise) {
    if (x.status === PENDING) {
      x.then(
        y => {
          resolvePromise(promise2, y, resolve, reject);
        },
        reason => {
          reject(reason);
        }
      );
    } else {
      x.then(resolve, reject);
    }
  } else if (x != null && (typeof x === "object" || typeof x === "function")) {
    try {
      let then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          reason => {
            if (called) return;
            called = true;
            reject(reason);
          }
        );
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}

Promise.prototype.then = function(onFulfilled, onRejected) {
  const that = this;
  let newPromise;
  onFulfilled =
    typeof onFulfilled === "function" ? onFulfilled : value => value;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : reason => {
          throw reason;
        };

  if (that.status === FULFILLED) {
    return (newPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          let x = onFulfilled(that.value);
          resolvePromise(newPromise, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    }));
  }

  if (that.status === REJECTED) {
    return (newPromise = new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          let x = onRejected(that.reason);
          resolvePromise(newPromise, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    }));
  }

  if (that.status === PENDING) {
    return (newPromise = new Promise((resolve, reject) => {
      that.onFulfilledCallbacks.push(value => {
        try {
          let x = onFulfilled(value);
          resolvePromise(newPromise, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
      that.onRejectedCallbacks.push(reason => {
        try {
          let x = onRejected(reason);
          resolvePromise(newPromise, x, resolve, reject);
        } catch (e) {
          reject(e);
        }
      });
    }));
  }
};
複製代碼
相關文章
相關標籤/搜索