Promise原理分析一

Promise原理分析一

Promise對象用於異步計算。一個Promise對象表明着一個還未完成,但預期未來會完成的操做。git

Note:es6

Promise對象有如下幾種狀態:segmentfault

  • pending: 初始狀態, 既不是 fulfilled 也不是 rejected.設計模式

  • fulfilled: 成功的操做.promise

  • rejected: 失敗的操做.異步

pending狀態的Promise對象既可轉換爲帶着一個成功值的fulfilled狀態,也可變爲帶着一個失敗信息的 rejected狀態。當狀態發生轉換時,Promise.then綁定的方法就會被調用。(當綁定方法時,若是 Promise對象已經處於fulfilled或rejected狀態,那麼相應的方法將會被馬上調用,因此在異步操做的完成狀況和它的綁定方法之間不存在競爭條件。)函數

由於Promise.prototype.then和Promise.prototype.catch方法返回Promises對象, 因此它們能夠被鏈式調用。ui

狀態轉換圖

constructor

說明

語法this

new Promise(executor);
new Promise(function(resolve, reject) { ... });

參數

name desc
executor 帶有resolve、reject兩個參數的函數對象。第一個參數用在處理執行成功的場景,第二個參數則用在處理執行失敗的場景。一旦咱們的操做完成便可調用這些函數。

實現

構造函數主要完成狀態的初始化,並提供resolvereject兩個方法給建立者以轉變狀態。

const utils = require('./utils');
const isFunction = utils.isFunction;

const STATUS = {
  PENDING: 'pending',
  FULFILLED: 'fulfilled',
  REJECTED: 'rejected'
};

const SYMBOL_STATUS = Symbol('status');
const SYMBOL_DATA = Symbol('data');
const SYMBOL_FULFILLED_CALLBACK = Symbol('fulfilledCallback');
const SYMBOL_REJECTED_CALLBACK = Symbol('rejectedCallback');

class Promise {
  constructor(executor) {
    if (!isFunction(executor)) {
      throw Error(`Promise executor ${executor} is not a function`);
    }

    const self = this;
    // 初始狀態爲pending
    const status = self[SYMBOL_STATUS] = STATUS.PENDING;

    // 使用symbol實現私有化
    self[SYMBOL_FULFILLED_CALLBACK] = undefined;
    self[SYMBOL_REJECTED_CALLBACK] = undefined;
    self[SYMBOL_DATA] = undefined;

    function resovle(value) {
      // todo
    }

    function reject(reason) {
      // todo
    }

    // 執行executor函數,若出現異常則調用reject方法,將狀態變爲rejected,同時調用回調函數
    try {
      executor(resovle, reject);
    } catch (err) {
      reject(err);
    }
  }
}

上面代碼中完成了構造函數的雛形,而resolvereject兩個函數的職責爲狀態轉變和回調函數的調用:

resolve,接受一個成功值,傳遞給綁定的fulfilled回調函數中。主要工做是將當前狀態變爲fulfilled狀態,同時調用綁定的fulfilled回調函數:

function resovle(value) {
  const fulfilledCallback = self[SYMBOL_FULFILLED_CALLBACK];

  if (status === STATUS.PENDING) {
    self[SYMBOL_STATUS] = STATUS.FULFILLED;  // 狀態轉換
    self[SYMBOL_DATA] = value;  // 保存成功值

    if (isFunction(fulfilledCallback)) {
      setTimeout(() => {  // 不阻塞主流程,在下一個事件輪詢中再調用fulfilled回調函數
        fulfilledCallback(value);
      });
    }
  }
}

reject,接受一個失敗信息,傳遞給綁定的rejected回調函數中。主要工做是將當前狀態變爲rejected 狀態,同時調用綁定的rejected回調函數:

function reject(reason) {
  const rejectedCallback = self[SYMBOL_REJECTED_CALLBACK];

  if (status === STATUS.PENDING) {
    self[SYMBOL_STATUS] = STATUS.REJECTED;  // 狀態轉換
    self[SYMBOL_DATA] = reason;  // 保存成功值

    if (isFunction(rejectedCallback)) {
      setTimeout(() => {  // 不阻塞主流程,在下一個事件輪詢中再調用rejected回調函數
        rejectedCallback(reason);
      });
    }
  }
}

fulfilled`和rejected回調函數是經過Promise.prototype.then和Promise.prototype.catch註冊的。

then

說明

then方法返回一個Promise。它有兩個參數,分別爲Promise在成功和失敗狀況下的回調函數。

語法

p.then(onFulfilled, onRejected);

p.then(function(value) {
  // todo
}, function(reason) {
  // todo
});

參數

name desc
onFulfilled 一個函數,當Promise的狀態爲fulfilled時調用。該函數有一個參數,爲成功的返回值。
onRejected 一個函數,當Promise的狀態爲rejected時調用。該函數有一個參數,爲失敗的緣由。

實現

根據當前狀態對回調函數進行處理,同時返回一個新的Promise對象,以便鏈式調用。

then(onFulfilled, onRejected) {
  const self = this;
  const status = self[SYMBOL_STATUS];
  const data = self[SYMBOL_DATA];
  const resolveValue = self[SYMBOL_RESOLVE_VALUE];
  // 若是onFulfilled不是函數,回調函數僅返回成功值
  const fulfilledCallback = isFunction(onFulfilled)
      ? onFulfilled
      : function returnFunc(value) { return value; };
  // 若是onRejected不是函數,回調函數僅返回失敗信息
  const rejectedCallback = isFunction(onRejected)
      ? onRejected
      : function throwFunc(reason) { throw reason; };
      
  // 當前狀態爲pending
  if (status === STATUS.PENDING) {
    // 返回一個新的Promise對象,能夠被鏈式調用
    return new Promise((resolve, reject) => {
      // 將fulfilled回調函數註冊到當前Promise對象中(非新Promise對象)
      self[SYMBOL_FULFILLED_CALLBACK] = value => {
        // 根據回調函數的執行狀況,經過傳遞的新Promise對象的resolve和reject方法對其狀態進行轉變
        try {
          const newValue = fulfilledCallback(value);
          // 解析成功值
          resolveValue(newValue, resolve, reject);
        } catch (err) {
          reject(err);
        }
      };

      // 同上
      self[SYMBOL_REJECTED_CALLBACK] = reason => {
        try {
          const newReason = rejectedCallback(reason);
          resolveValue(newReason, resolve, reject);
        } catch (err) {
          reject(err);
        }
      };
    });
  }

  // 當前狀態爲fulfilled
  if (status === STATUS.FULFILLED) {
    // 返回一個新的Promise對象,能夠被鏈式調用
    return new Promise((resolve, reject) => {
      // 在下一個事件輪詢中當即調用fulfilled回調函數,根據執行狀況決定新Promise對象的狀態轉變
      setTimeout(() => {
        try {
          const newValue = fulfilledCallback(data);
          resolveValue(newValue, resolve, reject);
        } catch (err) {
          reject(err);
        }
      });
    });
  }

  // 當前狀態爲rejected
  if (status === STATUS.REJECTED) {
    // 返回一個新的Promise對象,能夠被鏈式調用
    return new Promise((resolve, reject) => {
      // 在下一個事件輪詢中當即調用rejected回調函數,根據執行狀況決定新Promise對象的狀態轉變
      setTimeout(() => {
        try {
          const newReason = rejectedCallback(data);
          resolveValue(newReason, resolve, reject);
        } catch (err) {
          reject(err);
        }
      });
    });
  }
}

// 解析傳遞值函數
[SYMBOL_RESOLVE_VALUE](value, resolve, reject) {
  // 若傳遞值爲Promise對象,將新Promise對象的resolve和reject傳遞給Promise傳遞值以觸發狀態的轉換
  if (value instanceof Promise) {
    value.then(resolve, reject);
    return;
  }

  // 若傳遞值不是Promise對象,傳遞給resolve方法
  resolve(value);
}

根據當前Promise對象的狀態,對回調函數進行註冊或當即執行,同時返回一個新的Promise對象。

  • pending,註冊回調函數到當前的Promise對象中

  • fulfilledrejected,當即執行回調函數

catch

說明

catch方法只處理Promise被拒絕的狀況,並返回一個Promise。該方法的行爲和調用Promise.prototype.then(undefined, onRejected)相同。

語法

p.catch(onRejected);

p.catch(function(reason) {
   // todo
});

參數

name desc
onRejected 當Promise被拒絕時調用的函數。該函數調用時會傳入一個參數:失敗緣由。

實現

catch方法是對then方法的包裝,僅傳遞onRejected失敗回調函數。

catch(onRejected) {
  return this.then(null, onRejected);
}

總結

如今回顧下Promise的實現過程,其主要使用了設計模式中的觀察者模式:

  • 經過Promise.prototype.then和Promise.prototype.catch方法將觀察者方法註冊到被觀察者Promise對象中,同時返回一個新的Promise對象,以即可以鏈式調用。

  • 被觀察者管理內部pending、fulfilled和rejected的狀態轉變,同時經過構造函數中傳遞的resolve和reject方法以主動觸發狀態轉變和通知觀察者。

關鍵知識點

Promise

MDN

symbol

ruanyifeng

觀察者模式

百度百科

資源

完整代碼

相關文章
相關標籤/搜索