記閱讀promise-polyfill源碼探究Promise運行原理

Promise能夠看做一個異步操做的佔位符,表示一個異步操做的最終狀態(完成或失敗),以及其返回的值,是目前流行Javascript異步編程解決方案之一。git

網上關於Promise用法的文章汗牛充棟,做者在本文也不贅述了。Promise用久了不免會對其內部運行原理感到好奇,做者曾試着遵循Promise/A+規範本身寫一個promise的補丁,無奈野心被能力實力打臉,冷靜以後 去github上找promise的polyfill學習觀摩,promise-polyfill做爲一個輕量級、設計巧妙的polyfill讓做者大開眼界,仔細觀摩學習以後,寫成本篇小記與你們分享,錯漏之處懇求指點。github

一 .框架

做者將不會按照promise-polyfill的源碼一行行進行講解,而爲了閱讀方便將使用ES6/7的語法從新搭好一個框架,慢慢往裏面塞promise-polyfill核心代碼面試

咱們將搭好下面的代碼架構,先實現除了constructor、then的其餘方法找找感受,而後慢慢往裏加promise-polyfill的核心代碼,不耐煩的同窗能夠直接跳到下文 核心 部分閱讀編程

class Promise {
    constructor (executor) { ... }
    then (onFulfilled, onFulfilled) { ... }
    catch (onRejected) { ... }
    finally(callback) { ... }
    static resolve(value) { ... }
    static reject(reason) { ... }
    static all(iterable) { ... }
    static race(iterable) { ... }
}
複製代碼

先僞裝已經實現了 constructor、then 的核心功能c#

1. catch

/** * catch很簡單,就是then(null, onRejected)的語法糖 */

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

複製代碼

2.finally

/*** ** finally老是執行回調 ***/
finally (callback) {
    return this.then(
        function(value) {
            return Promise.resolve(callback()).then(() => value)
        },
        function(reason) {
            return Promise.resolve(callback()).then(() => Promise.reject(reason))
        }
    )
}

// tips

prom.finally(() => { «statements» })

等於

prom.then(result => {
    «statements»
    return result
}, error => {
    «statements»
    throw error
})
複製代碼

3. resolve

/** * 靜態resolve方法返回一個Promise對象 */
static resolve (value) {
  if (value && typeof value === 'object' && value.constructor === Promise) {
    return value  
  }
  return new Promise(function(resolve, reject) {
    resolve(value)
  })
}

複製代碼

4. reject

static reject (reason) {
    return new Promise(function (resolve, reject) {
      reject(reason)
    })
  }
複製代碼

5. all

/** **** 簡單起見,並未按照promise-polyfill的all方法,面試中也常常出現要求實現一個Promise.all方法 **/
static all (iterable) {
    return new Promise((resolve, reject) => {
      let count = 0, result = [], len = iterable.length
      if (len === 0) {
          resolve(result)
      }
      
      function resolver (i) {
          return function (x) {
              result[i] = x
              count++
              if (count === i) {
                  resolve(result)
              }
          }
      }
      for(let i = 0; i < len; i++) {
          Promise.resolve(iterable[i]).then(resolver(i), reject)
      }
    })
}

複製代碼

6. race

/** **** 誰的異步時間短,誰先處理,其他忽略 ***/
static race(iterable) {
    return new Promise((resolve, reject) => {
        for (let i = 0, len = iterable.length ; i < len; i++ ) {
            Promise.resolve(iterable[i]).then(resolve, reject)
        }
    })
}

複製代碼

二. 核心

爲了不沒必要要的干擾,省略代碼將以 @code_{function_name}_{number}標號。部分優化、兼容代碼將省略,例如對setTimeout的優化,將直接使用setTimeout,並不會影響代碼邏輯promise

1. constructor

/** * @constructor 初始化代碼 * 1. 初始化代碼進行常規的類型檢查和屬性賦值 * 2. @_state狀態 * penging <===> 0 * fulfilled <===> 1 * rejected <===> 2 * <Promise> <===> 3 (自定義的state,用於取<Promise>內部的值,在下文詳解) * 3.@_handled標記當前Promise實例的結果是否被處理過,初始化爲false * 3.@_deferreds 存放在then中的註冊的函數,元素形式{onFulfilled, onRejected,promise} * 4.@doResolve 詳見下文 **/
constructor (executor) {
  if (!(this instanceof Promise))
    throw new TypeError('Promises must be constructed via new')
  if (typeof fn !== 'function') throw new TypeError('not a function')
  /** @type {!number} */
  this._state = 0
  /** @type {!boolean} */
  this._handled = false
  /** @type {Promise|undefined} */
  this._value = undefined
  /** @type {!Array<!Function>} */
  this._deferreds = []

  doResolve(fn, this)
}

/*** ** @doResolve分發self給resolve和reject進行狀態轉換 ** @done確保當前實例的狀態由penging=>fulfilled 或者 penging=>rejected只發生一次 ** @resolve、 reject 全局方法,處理狀態轉換 **/

function doResolve (fn, self) {
  var done = false
  try {
    fn(
      function (value) {
        if (done) return
        done = true
        resolve(self, value)
      },
      function (reason) {
        if (done) return
        done = true
        reject(self, reason)
      }
    )
  } catch (ex) {
    if (done) return
    done = true
    reject(self, ex)
  }
}

複製代碼

2. then

executor執行完同步代碼,緊接着將在then中註冊方法onFulfilled、onRejected掛載到一個deferred對象上架構

// 空函數
const noop = function() {}

/** ** 1. 先建立一個Promise實例prom ** 2. new Handler(onFulfilled, onRejected, prom) 建立一個deferred對象掛載then上註冊的方法和 下個promise實例prom <deferred: {onFulfilled, onRejected, promise}> ** 3. handle在此將 deferred加入到當前promise實例的 _deferreds隊列上 ** 4. 返回Promise的實例prom,供後面的then、catch、繼續註冊方法 ** 5. prom不會在乎第一個promise是成功仍是失敗(以此類推)。 ** prom在乎第一個promise的處理函數(onFulfilled,onRejected)( 無論成功仍是失敗) ***/
then(onFulfilled, onRejected) {
    let prom = new this.constructor(noop)
    handle(this, new Handler(onFulfilled, onRejected, prom))
    return prom
}

/** *** @Handler 建立一個deferred <deferred: {onFulfilled, onRejected, promise}> ** onFulfilled, onRejected不是函數則賦值爲null,方便在handle中統一處理 **/
function Handler (onFulfilled, onRejected, promise) {
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
  this.onRejected = typeof onRejected === 'function' ? onRejected : null
  this.promise = promise
}

/*** *** then方法中調用handle就只是爲了將deferred添加到當前Promise實例的_deferreds上,等待最後處理Promise的返回值 ***/
function handle (self, deferred) {
    // ...@code_handle_1
    if (self._state === 0) {
        self._deferreds.push(deferred)
        return
    }
    // ...@code_handle_2
}
複製代碼

3. Promise結果處理

咱們先將假設Promise的狀態已發生改變,如何改變的先不表。promise-polyfill把結果統一在finale中處理app

/*** **** @function finale *** 1. 當前實例在rejected狀態且未註冊deferred,註冊一個定時器在下個事件循環中檢查Promise的值是否被調用過 *** 2. 循環調用調用handle,用當前實例的_deferreds隊列中註冊的方法處理promise的返回值 *** 3. 釋放當前實例的_deferreds的空間 ***/
function finale (self) {
  if (self._state === 2 && self._deferreds.length === 0) {
    setTimeout(function () { // 原代碼有優化,此處代碼改動,但邏輯未變
      if (!self._handled) {
        if (typeof console !== 'undefined' && console) {
          console.warn('Possible Unhandled Promise Rejection:', self.__value)// eslint-disable-line no-console
        }
      }
    })
  }
  
  for (var i = 0, len = self._deferreds.length; i < len; i++) {
    handle(self, self._deferreds[i])
  }
  self._deferreds = null
}


/** ** @function handle ** 1. while循環檢查當前實例的值是不是一個Promise實例,self = self._value直到self的值不是promise ** 2. self._state === 0,主要用在then方法中使用在添加deferred對象 ** 3. 標識當前promise實例已調用 ** 4. 啓動一個定時器,註冊方法在下個循環中處理當前promise結果 **/

function handle (self, deferred) {
  while (self._state === 3) {
    self = self._value
  }
  if (self._state === 0) {
    self._deferreds.push(deferred)
    return
  }
  self._handled = true
  setTimeout(function () {
  // 根據當前promise狀態,取掛載在deferred對象上的對應方法(在then方法中註冊的方法)
    var cb = self._state === 1 ? deferredeferred.onFulfilled : deferred.onRejected
    // 若是cb==null,則表示未在then中註冊方法,直接根據當前promise的狀態,調用resolve或reject,將當前值放到下一個then註冊的方法中,退出當前調用棧
    if (cb === null) {
      (self._state === 1 ? resolve : reject)(deferred.promise, self._value)
      return
    }
    var ret
    try {
    // 由then中註冊的方法調用當前promise的值,返回值放入ret
      ret = cb(self._value)
    } catch (e) {
    // 若是有異常將異常結果做爲當前實例then註冊方法的值傳入下個promise供then註冊的onRejected方法調用
      reject(deferred.promise, e)
      return
    }
    // 將ret做爲當前promise實例then方法中的值傳入下個promise的then註冊的onFulfilled方法調用
    resolve(deferred.promise, ret)
  })
}

複製代碼

4.全局resolve、reject函數

非Promise靜態方法,用做處理Promise的狀態轉移框架

/** ******@function resolve ** 處理 penging ===> fulfilled/rejected的狀態轉移 ** 1. 檢查Promise實例是否resolve調用自身,報錯 轉rejected ** 2. 檢查newValue返回值是不是一個Promse實例,設置私有狀態3(最後將在handle中解析成非promise值),傳遞給finale進一步處理 ** 3. 檢查newValue是不是一個thenable類型,調用doResolve ** 4. 非Promise的值,state設爲1,傳遞給finale進一步處理 ***/
function resolve (self, newValue) {
  try {
    // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
    // 對應規範2.3.1: 若是promise(self)和x(newValue)引用同一個對象,請以promisea TypeError爲理由拒絕。
    if (newValue === self) { throw new TypeError('A promise cannot be resolved with itself.') }
    if (
      newValue &&
      (typeof newValue === 'object' || typeof newValue === 'function')
    ) {
      var then = newValue.then
      if (newValue instanceof Promise) {
        self._state = 3
        self._value = newValue
        finale(self)
        return
      } else if (typeof then === 'function') {
      /** ** 對應規範 2.3.3.3 若是then是函數,請使用x as this,first參數resolvePromise和第二個參數調用它rejectPromise **/
        doResolve(bind(then, newValue), self)
        return
      }
    }
    self._state = 1
    self._value = newValue
    finale(self)
  } catch (e) {
    reject(self, e)
  }
}

/** ** Polyfill for Function.prototype.bind **/
function bind (fn, thisArg) {
  return function () {
    fn.apply(thisArg, arguments)
  }
}

/*** * 修改狀態,傳遞給finale進一步處理 ***/
function reject (self, newValue) {
  self._state = 2
  self._value = newValue
  finale(self)
}

複製代碼

結尾

總算水完了,看着好像也沒啥乾貨,錯漏之處懇求指點。異步

相關文章
相關標籤/搜索