摘要:從引擎剖析實現(xiàn)本文閱讀的源碼為,此版本的實現(xiàn)為版本,在后續(xù)版本繼續(xù)對其實現(xiàn)進行了處理。引入了語法等,在版本迭代后,逐漸迭代成了版本實現(xiàn)。方法的調(diào)用暴露給外部的高版本為方法,如果當前的指向的構(gòu)造函數(shù),則設(shè)置當前的狀態(tài),否則調(diào)用函數(shù)執(zhí)行。
從Google V8引擎剖析Promise實現(xiàn)
? 本文閱讀的源碼為Google V8 Engine v3.29.45,此版本的promise實現(xiàn)為js版本,在后續(xù)版本Google繼續(xù)對其實現(xiàn)進行了處理。引入了es6語法等,在7.X版本迭代后,逐漸迭代成了C版本實現(xiàn)。Define variables? 貼上源碼地址:https://chromium.googlesource... 大家自覺傳送。
? 代碼中所有類似%functionName的函數(shù)均是C語言實現(xiàn)的運行時函數(shù)。
首先定義了將要在JS作用域使用了一些變量,提高了編譯器的效率。
var IsPromise; var PromiseCreate; var PromiseResolve; var PromiseReject; var PromiseChain; var PromiseCatch; var PromiseThen; var PromiseHasRejectHandler;
隨后定義了一些全局私有變量供給和C語音交互,用于維護Promise的狀態(tài)和進行Debug。
var promiseStatus = GLOBAL_PRIVATE("Promise#status"); var promiseValue = GLOBAL_PRIVATE("Promise#value"); var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve"); var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject"); var promiseRaw = GLOBAL_PRIVATE("Promise#raw"); var promiseDebug = GLOBAL_PRIVATE("Promise#debug"); var lastMicrotaskId = 0;
其中GLOBAL_PRIVATE是python進行實現(xiàn)的,運用python的宏定義(macro)來定義調(diào)用了C語言的CreateGlobalPrivateOwnSymbol方法。
macro GLOBAL_PRIVATE(name) = (%CreateGlobalPrivateOwnSymbol(name));
隨后運用了一個自執(zhí)行的匿名函數(shù)進行閉包邏輯。
(function() { // 主邏輯 })();
在閉包邏輯的最后,在promise原型上掛載了三個方法:chain,then,catch。在promise對象上掛載了all,race等六個方法。將Promise對象注冊到了global。
%AddNamedProperty(global, "Promise", $Promise, DONT_ENUM); InstallFunctions($Promise, DONT_ENUM, [ "defer", PromiseDeferred, "accept", PromiseResolved, "reject", PromiseRejected, "all", PromiseAll, "race", PromiseOne, "resolve", PromiseCast ]); InstallFunctions($Promise.prototype, DONT_ENUM, [ "chain", PromiseChain, "then", PromiseThen, "catch", PromiseCatch ]);Start from constructor
var $Promise = function Promise(resolver) { // 如果傳入?yún)?shù)為全局promiseRaw變量的時候return if (resolver === promiseRaw) return; // 如果當前函數(shù)不是構(gòu)造函數(shù)的化,拋出錯誤這不是一個promise if (!%_IsConstructCall()) throw MakeTypeError("not_a_promise", [this]); // 如果傳入?yún)?shù)不是一個函數(shù)的話,拋出錯誤,傳入?yún)?shù)不是一個function if (!IS_SPEC_FUNCTION(resolver)) throw MakeTypeError("resolver_not_a_function", [resolver]); var promise = PromiseInit(this); try { // debug相關(guān)忽略 %DebugPushPromise(promise); resolver(function(x) { PromiseResolve(promise, x) }, function(r) { PromiseReject(promise, r) }); } catch (e) { // 報錯之后走到錯誤處理函數(shù) PromiseReject(promise, e); } finally { // debug相關(guān)忽略 %DebugPopPromise(); } }
構(gòu)造函數(shù)在做完額外的異常和參數(shù)判斷后,進入主邏輯調(diào)用PromiseInit方法初始化promise,隨后調(diào)用了resolver方法,傳入了兩個默認的處理函數(shù)。在promise在內(nèi)部被調(diào)用時(PromiseDeferred方法被調(diào)用時)會實例化$promise,將默認方法return回去,使得創(chuàng)建的promise示例具有resolve和reject方法。
function PromiseDeferred() { if (this === $Promise) { // Optimized case, avoid extra closure. var promise = PromiseInit(new $Promise(promiseRaw)); return { promise: promise, resolve: function(x) { PromiseResolve(promise, x) }, reject: function(r) { PromiseReject(promise, r) } }; } else { var result = {}; result.promise = new this(function(resolve, reject) { result.resolve = resolve; result.reject = reject; }) return result; } }PromiseInit
function PromiseSet(promise, status, value, onResolve, onReject) { // macro SET_PRIVATE(obj, sym, val) = (obj[sym] = val); // 設(shè)置promise的狀態(tài),SET_PRIVATE只有在給已經(jīng)存在的對象設(shè)置已有屬性值的時候才會被調(diào)用 SET_PRIVATE(promise, promiseStatus, status); SET_PRIVATE(promise, promiseValue, value); SET_PRIVATE(promise, promiseOnResolve, onResolve); SET_PRIVATE(promise, promiseOnReject, onReject); // debug代碼忽略 if (DEBUG_IS_ACTIVE) { %DebugPromiseEvent({ promise: promise, status: status, value: value }); } return promise; } function PromiseInit(promise) { return PromiseSet( promise, 0, UNDEFINED, new InternalArray, new InternalArray) }
實質(zhì)上是調(diào)用了PromiseSet方法給promise設(shè)置了當前的狀態(tài)。
PromiseResolvepromiseResolve方法的調(diào)用暴露給外部的promise.accept(高版本為resolve)方法,如果當前的this指向promise的構(gòu)造函數(shù),則設(shè)置當前的promise狀態(tài),否則調(diào)用resolve函數(shù)執(zhí)行。
function PromiseResolved(x) { if (this === $Promise) { // Optimized case, avoid extra closure. return PromiseSet(new $Promise(promiseRaw), +1, x); } else { return new this(function(resolve, reject) { resolve(x) }); } }
promiseResolve處理邏輯同promiseReject,不再贅述。
promise.thenPromiseThen方法的調(diào)用暴露給實例化后的promise.then方法調(diào)用。
PromiseThen = function PromiseThen(onResolve, onReject) { onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve : PromiseIdResolveHandler; onReject = IS_SPEC_FUNCTION(onReject) ? onReject : PromiseIdRejectHandler; var that = this; var constructor = this.constructor; return %_CallFunction( this, function(x) { x = PromiseCoerce(constructor, x); return x === that ? onReject(MakeTypeError("promise_cyclic", [x])) : IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x); }, onReject, PromiseChain ); }
首先判斷傳入的兩個參數(shù)是否是函數(shù),不是的話添加默認的處理函數(shù),做良好的容錯處理。而后調(diào)用了 %_CallFunction方法(第一個參數(shù)是this,最后一個參數(shù)是要調(diào)用的方法,中間是傳入?yún)?shù)),類似Function.prototype.call()方法,調(diào)用了PromiseChain方法,傳入了兩個參數(shù)resolve和reject。在resolve方法內(nèi)部調(diào)用了PromiseCoerce方法,生成對象如果是個thenable對象調(diào)用對象的then方法否則直接onResolve方法。
PromiseCoercefunction PromiseCoerce(constructor, x) { if (!IsPromise(x) && IS_SPEC_OBJECT(x)) { var then; try { then = x.then; } catch(r) { return %_CallFunction(constructor, r, PromiseRejected); } // macro IS_SPEC_FUNCTION(arg) = (%_ClassOf(arg) === "Function"); // 如果是一個function if (IS_SPEC_FUNCTION(then)) { var deferred = %_CallFunction(constructor, PromiseDeferred); try { %_CallFunction(x, deferred.resolve, deferred.reject, then); } catch(r) { deferred.reject(r); } return deferred.promise; } } return x; }
核心的邏輯是如果傳入對象的then屬性是一個function,則調(diào)用then方法。若有報錯走到reject方法。
PromiseChainPromiseChain = function PromiseChain(onResolve, onReject) { // 補默認的處理函數(shù) onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; var deferred = %_CallFunction(this.constructor, PromiseDeferred); switch (GET_PRIVATE(this, promiseStatus)) { case UNDEFINED: throw MakeTypeError("not_a_promise", [this]); case 0: // Pending GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred); GET_PRIVATE(this, promiseOnReject).push(onReject, deferred); break; case +1: // Resolved PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred], +1); break; case -1: // Rejected PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred], -1); break; } // debug代碼忽略 if (DEBUG_IS_ACTIVE) { %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); } return deferred.promise; }
PromiseChain方法是promise實現(xiàn)的核心,判斷當前定義的promise狀態(tài),如果是pending狀態(tài)在promiseOnResolve數(shù)組中push當前的onResolve方法。如果是Resolved狀態(tài)或者Rejected狀態(tài),則調(diào)用PromiseEnqueue函數(shù)進行微任務(wù)的添加。
PromiseEnqueuefunction PromiseEnqueue(value, tasks, status) { var id, name, instrumenting = DEBUG_IS_ACTIVE; %EnqueueMicrotask(function() { if (instrumenting) { %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); } for (var i = 0; i < tasks.length; i += 2) { PromiseHandle(value, tasks[i], tasks[i + 1]) } if (instrumenting) { %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); } }); if (instrumenting) { id = ++lastMicrotaskId; name = status > 0 ? "Promise.resolve" : "Promise.reject"; %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); } }
? 此步驟其實是將PromiseHandle函數(shù)加入JS運行時的微任務(wù)隊列中。微任務(wù)的隊列列表是C語言進行維護的,應(yīng)用%EnqueueMicrotask方法進行添加。
PromiseHandlefunction PromiseHandle(value, handler, deferred) { try { %DebugPushPromise(deferred.promise); var result = handler(value); if (result === deferred.promise) throw MakeTypeError("promise_cyclic", [result]); else if (IsPromise(result)) %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); else deferred.resolve(result); } catch (exception) { try { deferred.reject(exception); } catch (e) { } } finally { %DebugPopPromise(); } }
此函數(shù)處理了傳入的方法,是指是調(diào)用了resolve方法,如果返回的結(jié)果依舊是一個promise則繼續(xù)調(diào)用PromiseChain方法,否則調(diào)用新生成的promise實例的resolve方法,進而實現(xiàn)循壞調(diào)用。
promise.allpromise的all方法實現(xiàn)了發(fā)送多個promise請求,返回一個新的promise,所有promise打到resolve狀態(tài)時觸發(fā)resolve狀態(tài),若有一個promise被reject,則返回此promise的reject原因。
function PromiseAll(values) { var deferred = %_CallFunction(this, PromiseDeferred); var resolutions = []; if (!%_IsArray(values)) { deferred.reject(MakeTypeError("invalid_argument")); return deferred.promise; } try { var count = values.length; if (count === 0) { deferred.resolve(resolutions); } else { for (var i = 0; i < values.length; ++i) { this.resolve(values[i]).then( (function() { // Nested scope to get closure over current i (and avoid .bind). // TODO(rossberg): Use for-let instead once available. var i_captured = i; return function(x) { resolutions[i_captured] = x; if (--count === 0) deferred.resolve(resolutions); }; })(), function(r) { deferred.reject(r) } ); } } } catch (e) { deferred.reject(e) } return deferred.promise; }
首先判斷傳參的合理性,生成一個新的promise對象,利用遍歷的方式給每個傳入的promise的resolve方法后都追加了then方法,使得每個傳入的promise執(zhí)行then方法后湊執(zhí)行判斷邏輯,當計數(shù)count的flag為0的時候,所有promise resolve完成,調(diào)用新promise對象的resolve方法,傳入新promise的reject方法作為then方法reject參數(shù)。使得所有promise的reject函數(shù)被調(diào)用時都會走到新promise對象的reject,最后返回新生成的promise。
SummaryPromise的狀態(tài)和核心變量均托管到公共的作用域去維護,通過數(shù)組的push方法去添加Promise自定的resolve和reject方法。并將resolve和reject方法的執(zhí)行加入微服務(wù)隊列中,等到resolve方法被調(diào)用時執(zhí)行resolve(value)方法進行調(diào)用。為了實現(xiàn)promise的循環(huán)嵌套調(diào)用,在每次處理value之前將處理邏輯之上包裹了一層新的promise邏輯,類似(new promise()).then(resolve(value)),思路如下。
var ref = function (value) { if (value && value.then) return value; return { then: function (callback) { // 實例化一個新的promise var result = defer(); // 進入宏任務(wù)隊列 enqueue(function () { result.resolve(callback(value)); }); return result.promise; } }; };Reference
promise設(shè)計思想:https://github.com/kriskowal/...
JavaScript執(zhí)行機制:https://www.jianshu.com/p/17c...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/109724.html
摘要:對于引擎來說,兩者獲取堆棧的方式是不同的。對于引擎來說,堆棧信息附加在了函數(shù)所返回的并在鏈中傳遞,這樣函數(shù)也能在需要的時候獲取堆棧信息。使用可以實時監(jiān)控線上應(yīng)用的錯誤,并獲取完整的堆棧信息。 譯者按: Async/Await真的只是簡單的語法糖嗎?No! 原文:Asynchronous stack traces: why await beats .then() 作者: Math...
摘要:參考文檔升級后的函數(shù)回調(diào)參數(shù)問題中的使用方法和還是不一樣的源碼講解的內(nèi)部機制優(yōu)化相關(guān)內(nèi)容文章官方文檔簡述使用過的都知道這個方法的作用,通過該方法會讓形式的函數(shù)風格轉(zhuǎn)換成方法,可以認為是一顆語法糖,例如接下來我們就分析一下這個的內(nèi)部流程。 參考文檔 升級bluebird 3后Promise.promisify的函數(shù)回調(diào)參數(shù)問題:3中的使用方法和2還是不一樣的 How does Bl...
摘要:歡迎來我的個人站點性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動及頁面渲染優(yōu)化理論寫法對壓縮率的影響唯快不破應(yīng)用的個優(yōu)化步驟進階鵝廠大神用直出實現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補課緩存機制優(yōu)化動 歡迎來我的個人站點 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動 scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...
摘要:歡迎來我的個人站點性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動及頁面渲染優(yōu)化理論寫法對壓縮率的影響唯快不破應(yīng)用的個優(yōu)化步驟進階鵝廠大神用直出實現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補課緩存機制優(yōu)化動 歡迎來我的個人站點 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動 scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...
閱讀 596·2023-04-26 01:42
閱讀 3230·2021-11-22 11:56
閱讀 2407·2021-10-08 10:04
閱讀 855·2021-09-24 10:37
閱讀 3136·2019-08-30 15:52
閱讀 1758·2019-08-29 13:44
閱讀 479·2019-08-28 17:51
閱讀 2151·2019-08-26 18:26