1. 程式人生 > >Promise原始碼閱讀之建構函式+then過程

Promise原始碼閱讀之建構函式+then過程

前言

Promise是非同步程式設計的一種方案,ES6規範中將其寫入規範標準中,統一了用法。
考慮到瀏覽器的相容性,Vue專案中使用promise,就具體閱讀promise原始碼,看看內部的具體實現。

具體分析

通過具體例項來閱讀promise原始碼的實現,例項如下:

new Promise(function (resolve, reject) {
  get('http://www.google.com', function (err, res) {
    if (err) reject(err);
    else resolve(res);
  })
}).then
(function(res) { // 相關處理 });

Promise庫的GitHub地址,實際上在原始碼中只需要關注下面幾個檔案即可(這幾個檔案實現了經常使用的API):

  • core.js
  • es6-extensions.js
  • finally.js

core.js

核心檔案,核心功能是:

提供Promise建構函式的定義以及resolve、reject的具體處理

ES6 Promises規範中,Promise代表非同步操作,其狀態有三個:

pending:進行中
fulfilled:已成功
rejected: 已失敗

因為promise中需要關注的程式碼量不是很多,就結合具體的程式碼進行。

Promise建構函式

Promise建構函式中的處理:

function Promise(fn) {
  // 防止Promise()作為函式呼叫
  // 瀏覽器中作為函式此處this === window,並沒有起到要達到的效果
  if (typeof this !== 'object') {
    throw new TypeError('Promises must be constructed via new');
  }
  // 必須要傳遞函式
  if (typeof fn !== 'function'
) { throw new TypeError('Promise constructor\'s argument is not a function'); } // Promise內部維護的狀態,預設為0即為pending,進行中 this._deferredState = 0; this._state = 0; this._value = null; this._deferreds = null; if (fn === noop) return; doResolve(fn, this); }
doResolve

在這裡插入圖片描述
從doResolve函式可知,內部實際上是呼叫tryCallTwo函式來處理,而tryCallTwo函式的處理實際上就是呼叫Promise中傳遞的fn函式,並將resolve和reject的回撥函式傳遞到fn中,原始碼如下:

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

這裡a就是表示處理resolve狀態的函式,相對應的b就是處理rejected狀態的函式

resolve

從上面邏輯中可知當執行resolve時,內部會呼叫已定義好的resolve函式來處相關邏輯。
在這裡插入圖片描述
這裡需要的點在getThen函式以及then結果的判斷,處理了三個情況:

  • getThen執行錯誤的處理
  • then是Promise物件的處理
  • then是函式的處理

為什麼要這麼處理這幾種情況,就需要去看getThen具體實現的功能,通過原始碼可知:

function getThen(obj) {
  try {
    return obj.then;
  } catch (ex) {
    LAST_ERROR = ex;
    return IS_ERROR;
  }
}

通過上面的邏輯處理可知:

resolve這邊實際上就是處理newValue,即執行了new Promise後之後傳遞給resolve的引數問題

主要的判斷引數是否存在並且是否是object或function,如果是object或function,則會呼叫getThen獲取then進行下面的判斷:

  • then是否等於IS_ERROR,即getThen是否執行出錯
  • newValue是否是Promise物件
  • then是否是函式

實際上上面的判斷都是處理newValue中是否存在then函式的問題。

只要newValue中不存在then函式,就會設定_state和_value,並呼叫finale函式。

reject函式

reject中處理邏輯就是設定state和value值,呼叫finale函式。

state設定為2,表示rejected狀態

finale函式

finale函式式實際上是處理fulfilled狀態和rejected,分別呼叫handle來處理相關邏輯。
當你使用new Promise實際上_deferredStat預設為0,而且在之前的處理邏輯中並沒有相關處理,因為Promise中是非同步操作實際上這邊是在then函式中處理。

then函式

Promise.prototype.then = function(onFulfilled, onRejected) {
  // 處理非Promise建構函式建立的情況,例如prototype繼承未修改constructor時
  if (this.constructor !== Promise) {
    return safeThen(this, onFulfilled, onRejected);
  }
  var res = new Promise(noop);
  handle(this, new Handler(onFulfilled, onRejected, res));
  return res;
};

從上面可知鏈式呼叫的實現以及then函式內部的主要處理是handle函式。
在這裡插入圖片描述

首先看看Handler建構函式的作用,實際上是then函式內部會構建promise物件的連結串列結構,Handler的屬性:

function Handler(onFulfilled, onRejected, promise){
  // then函式的resolve函式
  this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  // then函式的reject函式
  this.onRejected = typeof onRejected === 'function' ? onRejected : null;
  // 下一個promise物件,實際上就是then函式返回的
  this.promise = promise;
}

而handle中的處理邏輯主要點如下:

判斷上一個Promise中state,根據狀態設定_deferredState和_deferreds的值
根據上一個Promise中state不為0,就會執行handleResolved

實際上handle就是判斷上一個Promise中的非同步操作是否執行了,沒有執行就等待。

實際上上一個promise執行了,就會改變_state的值,而then函式根據_state的值來做相應的判斷

而在handleResolved函式中實際上會呼叫resolve或reject重複new Promise中一部分動作,這裡就需要通過例項去整體梳理這樣好理解些。

根據例項梳理整個過程如下:

  • new Promise(fn)構建promise例項物件,會立即執行fn函式
  • fn函式式get請求,這是一個非同步操作,而這個非同步操作的處理邏輯中會主動觸發resolve或reject,所以new Promise建立的promise會等待主動觸發resolve或reject
  • Promise等待,但是這邊的同步程式碼then函式就執行了,呼叫Promise.prototype.then暴露的API
  • then函式中就構建另一個Promise,建立Hander物件形成鏈式,並執行handle函式
  • handle函式執行就要判斷上一個Promise例項的_state(例項這裡是new Promise建立的),因為上一個Promise執行非同步操作的,_state為0
  • _state為0,就設定了_deferredState為1,然後等待上一個Promise執行完非同步操作

當例項中new Promise中的get請求成功後,就會觸發resolve,此時:

  • 會呼叫resolve函式,resolve函式中則會判斷傳遞進來的資料是否是物件或函式(即處理存在then函式的情況)
  • 如果不存在then函式的情況下,就設定_state以及_value值,並呼叫finale函式
  • finale函式中就判斷_deferredState值,在上一個Promise等待非同步操作中then函式就設定了該值為1,則會呼叫handle函式
  • 實際上呼叫handle就是去執行handleResolved,執行then函式傳遞進來的resolve函式,即使用asap來執行resolve函式(如果此時resolve是一個非同步操作,實際上就是重複new Promise中主動觸發resolve的過程),注意此時是另一個Promise物件了
  • 此時resolve的處理就是將上一個Promise傳遞進來的value值設定為下一個Promise的value值,並將當前的Promise的state狀態置為1
  • 此時會執行finale,實際上這個函式在此時沒有執行任何邏輯,因為此時_state為1,即使再接then函式,也不會設定_deferredState了,而是直接執行handleResolved,因為此步實際上是被asap主動執行了

如此反覆上面的過程。

以當時例項promise整個邏輯處理如下:
在這裡插入圖片描述