Promise 源碼分析

前言

then/promise項目是基於Promises/A+標準實現的Promise庫,從這個項目當中,咱們來看Promise的原理是什麼,它是如何作到的,從而更加熟悉Promise前端

分析

從index.js當中知道,它是先引出了./core.js,隨後各自執行了其餘文件的代碼,經過requeire的方法。node

咱們首先先想一下最基礎的promise用法git

new Promise((resolve, reject) =>  {
    resolve(4);

}).then(res => {
    console.log(res); // export 4
});

Promise中的標準

標準中規定:es6

  1. Promise對象初始狀態爲 Pending,在被 resolvereject 時,狀態變爲 FulfilledRejected
  2. resolve接收成功的數據,reject接收失敗或錯誤的數據
  3. Promise對象必須有一個 then 方法,且只接受兩個可函數參數 onFulfilledonRejected

index.jsgithub

'use strict';

module.exports = require('./core.js');
require('./done.js');
require('./finally.js');
require('./es6-extensions.js');
require('./node-extensions.js');
require('./synchronous.js');

咱們先看src/core.jsc#

function Promise(fn) {
  // 判斷 this必定得是object否則就會報錯,這個方法必定得要new出來
  if (typeof this !== 'object') {
    throw new TypeError('Promises must be constructed via new');
  }
  // 判斷fn 必定得是一個函數
  if (typeof fn !== 'function') {
    throw new TypeError('Promise constructor\'s argument is not a function');
  }
  this._deferredState = 0;
  this._state = 0;
  this._value = null;
  this._deferreds = null;
  if (fn === noop) return;
  // 最終doResolve很關鍵
  doResolve(fn, this);
}

Promise是一個構造方法,開始時,它進行了校驗,確保了fn是一個函數,隨後對一些變量進行了初始化,最後執行了doResolve()數組

咱們接着看doResolve這個方法。promise

/**
 * Take a potentially misbehaving resolver function and make sure
 * onFulfilled and onRejected are only called once.
 *
 * Makes no guarantees about asynchrony.
 */
// 
// 確保`onFulfilled`和`onRejected`方法只調用一次
// 不保證異步
function doResolve(fn, promise) {
  var done = false;
  var res = tryCallTwo(fn, function (value) {
    // 若是done 爲true 則return
    if (done) return;
    done = true;
    // 回調執行 resolve()
    resolve(promise, value);
  }, function (reason) {
    // 若是done 爲true 則return
    if (done) return;
    done = true;
    reject(promise, reason);
  });
  // res爲truCallTwo()的返回值
  // 若是done沒有完成 而且 res 是 `IS_ERROR`的狀況下
  // 也會執行reject(),同時讓done完成
  if (!done && res === IS_ERROR) {
    done = true;
    reject(promise, LAST_ERROR);
  }
}

doResolve最關鍵的是執行了tryCallTwo方法,這個方法的第二,第三個參數都是回調,當執行回調後,done爲true,同時各自會執行resolve()或者reject()方法。最後當tryCallTwo的返回值爲IS_ERROR時,也會執行reject()方法。app

咱們先來看一下tryCallTwo方法異步

function tryCallTwo(fn, a, b) {
  try {
    fn(a, b);
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

fn實際就是Promise初始化時的匿名函數(resolve, reject) => {}ab則表明的是resolve()reject()方法,當咱們正常執行完promise函數時,則執行的是resolve則在doResolve中,咱們當時執行的第二個參數被回調,若是報錯,reject()被執行,則第二個參數被回調。最後捕獲了異常,當發生了報錯時,會return IS_ERROR,非報錯時會return undinfed

再回到剛纔的doResolve方法,當執行了第二個參數的回調以後,會執行resolve方法

function resolve(self, newValue) {
  // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
  // 不能吃傳遞本身
  if (newValue === self) {
    // 報錯
    return reject(
      self,
      new TypeError('A promise cannot be resolved with itself.')
    );
  }
  // promise做爲參數
  if (
    newValue &&
    (typeof newValue === 'object' || typeof newValue === 'function')
  ) {
    // 獲取它的promise方法 讀取newValue.then
    var then = getThen(newValue);
    if (then === IS_ERROR) {
      // 若是then IS_ERROR
      return reject(self, LAST_ERROR);
    }
    if (
      // 若是then是self的then
      // 而且Promise
      then === self.then &&
      // newValue 屬於Promise
      newValue instanceof Promise
    ) {
      // _state爲3
      // 通常then以後走這裏
      // 執行then(newValue)返回了promise
      self._state = 3;
      // selft.value爲newValue
      self._value = newValue;
      // 當state爲3時執行 finale
      finale(self);
      return;
    } else if (typeof then === 'function') {
      doResolve(then.bind(newValue), self);
      return;
    }
  }
  self._state = 1;
  self._value = newValue;
  finale(self);
}

在沒有鏈式調用then的狀況下(也就是隻要一個then)的狀況下,會將內部狀態_state設置成3,將傳入值賦給內部變量_value最後會執行final()方法,否則則會使用doResolve來調用then

咱們再來看下reject

function reject(self, newValue) {
  // _state = 2爲reject
  self._state = 2;
  self._value = newValue;
  if (Promise._onReject) {
    Promise._onReject(self, newValue);
  }
  finale(self);
}

reject當中咱們的_state變動爲了2,一樣最後finale被調用。

咱們來看下finale函數

// 執行本身的deferreds
function finale(self) {
  if (self._deferredState === 1) {
    handle(self, self._deferreds);
    self._deferreds = null;
  }
  if (self._deferredState === 2) {
    for (var i = 0; i < self._deferreds.length; i++) {
      // 遍歷handle
      handle(self, self._deferreds[i]);
    }
    // 將deferred 置空
    self._deferreds = null;
  }
}

在該方法當中根據不一樣的_deferredState,會執行不一樣的handle方法。

咱們再來看handle方法

function handle(self, deferred) {
  while (self._state === 3) {
    self = self._value;
  }
  // 若是有onHandle方法 則執行該方法
  if (Promise._onHandle) {
    Promise._onHandle(self);
  }
  // (初始 _state 爲0)
  if (self._state === 0) {
    // (初始 _deferredState 爲0)
    if (self._deferredState === 0) {
      self._deferredState = 1;
      self._deferreds = deferred;
      return;
    }
    // 若是 _deferredState是1 則__deferreds是一個數組
    if (self._deferredState === 1) {
      self._deferredState = 2;
      self._deferreds = [self._deferreds, deferred];
      return;
    }
    // 當走到這裏 _deferredState應該是2 將deferred
    // 插入到數組當中
    self._deferreds.push(deferred);
    return;
  }
  handleResolved(self, deferred);
}

這裏比較關鍵的應該就是經過deferredState不一樣的狀態,將deferred放入deferreds當中。另外當咱們的_state不爲0時,最終會執行handleResolved

繼續看handleResolve()方法

function handleResolved(self, deferred) {
  asap(function() {
    // _state爲1時,cb = onFulfilled 不然 cb = onRejected
    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
    if (cb === null) {
      if (self._state === 1) {
        resolve(deferred.promise, self._value);
      } else {
        reject(deferred.promise, self._value);
      }
      return;
    }
    var ret = tryCallOne(cb, self._value);
    if (ret === IS_ERROR) {
      reject(deferred.promise, LAST_ERROR);
    } else {
      resolve(deferred.promise, ret);
    }
  });
}.then((res) => {
}).catch((error) => {
})

在這個方法當中,會根據咱們任務(_state)的不一樣狀態,來執行onFulfilled或者onRejected方法。當此方法調用時,也就是咱們一個簡單的Promise的結束。

回到剛纔說的Promise構造方法結束的時候

設置了Promise函數的一些變量

Promise._onHandle = null;
Promise._onReject = null;
Promise._noop = noop;

隨後在Promise的原型上設置了then方法。

Promise.prototype.then = function(onFulfilled, onRejected) {
  // 首先看這是誰構造的 若是不是promise
  // 則return 執行safeThen
  if (this.constructor !== Promise) {
    return safeThen(this, onFulfilled, onRejected);
  }
  // 若是是則初始化一個Promise 可是參數 noop 爲空對象 {}
  var res = new Promise(noop);
  // 隨後執行handle方法
  handle(this, new Handler(onFulfilled, onRejected, res));
  return res;
};

then這個方法中首先判斷了它是否由Promise構造的,若是不是,則返回並執行safeThen,否則則執行Promise構造一個res對象,而後執行handle方法,最後將promise變量res返回。handle方法以前有提過,在這裏,當初始化時_state_deferred的轉改都爲0,所以它會將defrred保存到promise當中。

先看一下上面說的safeThen方法

function safeThen(self, onFulfilled, onRejected) {
  return new self.constructor(function (resolve, reject) {
    var res = new Promise(noop);
    res.then(resolve, reject);
    handle(self, new Handler(onFulfilled, onRejected, res));
  });
}

流程

須要有一個Promise的構造方法,這個構造方法最終會執行它的參數(resolve, reject) => {},聲明的then方法會經過handle()方法將onFulfilledonRejected方法保存起來。當在外部調用resolve或者onRejected時,最終也會執行handle可是它,會最後根據狀態來執行onFulfilled或者onRejected。從而到咱們的then回調中。

Promise的擴展

done

done的擴展在src/done.js當中

'use strict';

var Promise = require('./core.js');

module.exports = Promise;
Promise.prototype.done = function (onFulfilled, onRejected) {
  var self = arguments.length ? this.then.apply(this, arguments) : this;
  self.then(null, function (err) {
    setTimeout(function () {
      throw err;
    }, 0);
  });
};

內部執行了then()

finally

finally的擴展在src/finally.js當中

Promise的標準當中,自己是沒有finally方法的,可是在ES2018的標準裏有,finally的實現以下

'use strict';

var Promise = require('./core.js');

module.exports = Promise;
Promise.prototype.finally = function (callback) {
  return this.then(function (value) {
    return Promise.resolve(callback()).then(function () {
      return value;
    });
  }, function (err) {
    return Promise.resolve(callback()).then(function () {
      throw err;
    });
  });
};

PromiseonFulfilledonRejected 無論回調的哪一個,最終都會觸發callback 回調。還要注意的一點是finally的返回也是一個Promise

es6-extensions.js

es6-extensions.js文件當中包含了ES6的一些擴展。

Promise.resolve

function valuePromise(value) {
  var p = new Promise(Promise._noop);
  // 將_state賦值爲 非0
  // _value進行保存
  p._state = 1;
  p._value = value;
  // 這樣作的目的是省略的一些前面的邏輯
  return p;
}

Promise.resolve = function (value) {
  if (value instanceof Promise) return value;

  if (value === null) return NULL;
  if (value === undefined) return UNDEFINED;
  if (value === true) return TRUE;
  if (value === false) return FALSE;
  if (value === 0) return ZERO;
  if (value === '') return EMPTYSTRING;

  // value return new Promise
  if (typeof value === 'object' || typeof value === 'function') {
    try {
      var then = value.then;
      if (typeof then === 'function') {
        // 返回 返回了一個新的Promise對象
        return new Promise(then.bind(value));
      }
    } catch (ex) {
        // 若是報錯 則返回一個就只
      return new Promise(function (resolve, reject) {
        reject(ex);
      });
    }
  }

  return valuePromise(value);
};

Promise.reject

Promise.reject = function (value) {
  return new Promise(function (resolve, reject) {
    reject(value);
  });
};

Promise.all

Promise.all = function (arr) {
  // 相似深拷貝了一份給了args
  var args = Array.prototype.slice.call(arr);

  return new Promise(function (resolve, reject) {
    // 判斷了all的promise數量
    if (args.length === 0) return resolve([]);
    // remaining則是promise數組的長度
    var remaining = args.length;
    // i爲index val 爲 promise
    function res(i, val) {
      if (val && (typeof val === 'object' || typeof val === 'function')) {
        if (val instanceof Promise && val.then === Promise.prototype.then) {
          while (val._state === 3) {
            val = val._value;
          }
          if (val._state === 1) return res(i, val._value);
          if (val._state === 2) reject(val._value);
          // val._state 爲 0時 走這裏
          val.then(function (val) {
            res(i, val);
          }, reject);
          return;
        } else {
          var then = val.then;
          if (typeof then === 'function') {
            var p = new Promise(then.bind(val));
            p.then(function (val) {
              res(i, val);
            }, reject);
            return;
          }
        }
      }
      args[i] = val;
      // 當全部的promise執行完 則是remaining爲0
      // 則執行resolve();
      if (--remaining === 0) {
        resolve(args);
      }
    }
    // 遍歷全部的promise
    for (var i = 0; i < args.length; i++) {
      res(i, args[i]);
    }
  });
};

Promise.all()返回的也是一個Promise函數。
內部有一個remaining變量每當執行完一個promise函數後就會減一,當全部promise執行完,會執行本身的resolve

Promise.race

Promise.race = function (values) {
  return new Promise(function (resolve, reject) {
    values.forEach(function(value){
      Promise.resolve(value).then(resolve, reject);
    });
  });
};

遍歷傳入的promise數組,通過Promise.resolve(value)的源碼能夠看到,若是value是一個Promise則戶直接將這個value返回,最後數組中的promise哪一個優先回調即執行。

Promise.property.catch

catch在標準當中也是沒有,雖然咱們用的比較多

Promise.prototype['catch'] = function (onRejected) {
  return this.then(null, onRejected);
};

catch的回調實際是then(null, onRejected)的回調。

廣而告之

本文發佈於薄荷前端週刊,歡迎Watch & Star ★,轉載請註明出處。

歡迎討論,點個贊再走吧 。◕‿◕。 ~

相關文章
相關標籤/搜索