從Google V8引擎剖析Promise實現

從Google V8引擎剖析Promise實現

​ 本文閱讀的源碼爲Google V8 Engine v3.29.45,此版本的promise實現爲js版本,在後續版本Google繼續對其實現進行了處理。引入了es6語法等,在7.X版本迭代後,逐漸迭代成了C版本實現。

​ 貼上源碼地址:https://chromium.googlesource... 你們自覺傳送。javascript

​ 代碼中全部相似%functionName的函數均是C語言實現的運行時函數。java

Define variables

首先定義了將要在JS做用域使用了一些變量,提升了編譯器的效率。python

var IsPromise;
var PromiseCreate;
var PromiseResolve;
var PromiseReject;
var PromiseChain;
var PromiseCatch;
var PromiseThen;
var PromiseHasRejectHandler;

隨後定義了一些全局私有變量供給和C語音交互,用於維護Promise的狀態和進行Debug。git

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進行實現的,運用python的宏定義(macro)來定義調用了C語言的CreateGlobalPrivateOwnSymbol方法。es6

macro GLOBAL_PRIVATE(name) = (%CreateGlobalPrivateOwnSymbol(name));

隨後運用了一個自執行的匿名函數進行閉包邏輯。github

(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) {
    // 若是傳入參數爲全局promiseRaw變量的時候return
    if (resolver === promiseRaw) return;
    // 若是當前函數不是構造函數的化,拋出錯誤這不是一個promise
    if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]);
    // 若是傳入參數不是一個函數的話,拋出錯誤,傳入參數不是一個function
    if (!IS_SPEC_FUNCTION(resolver))
        throw MakeTypeError('resolver_not_a_function', [resolver]);
    var promise = PromiseInit(this);
    try {
        // debug相關忽略
        %DebugPushPromise(promise);
        resolver(function(x) { PromiseResolve(promise, x) },
                 function(r) { PromiseReject(promise, r) });
    } catch (e) {
        // 報錯以後走到錯誤處理函數
        PromiseReject(promise, e);
    } finally {
        // debug相關忽略
        %DebugPopPromise();
    }
}

構造函數在作完額外的異常和參數判斷後,進入主邏輯調用PromiseInit方法初始化promise,隨後調用了resolver方法,傳入了兩個默認的處理函數。在promise在內部被調用時(PromiseDeferred方法被調用時)會實例化$promise,將默認方法return回去,使得建立的promise示例具備resolve和reject方法。promise

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);
    // 設置promise的狀態,SET_PRIVATE只有在給已經存在的對象設置已有屬性值的時候纔會被調用
    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)
}

實質上是調用了PromiseSet方法給promise設置了當前的狀態。閉包

PromiseResolve

promiseResolve方法的調用暴露給外部的promise.accept(高版本爲resolve)方法,若是當前的this指向promise的構造函數,則設置當前的promise狀態,不然調用resolve函數執行。函數

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.then

PromiseThen方法的調用暴露給實例化後的promise.then方法調用。

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
    );
}

首先判斷傳入的兩個參數是不是函數,不是的話添加默認的處理函數,作良好的容錯處理。然後調用了 %_CallFunction方法(第一個參數是this,最後一個參數是要調用的方法,中間是傳入參數),相似Function.prototype.call()方法,調用了PromiseChain方法,傳入了兩個參數resolve和reject。在resolve方法內部調用了PromiseCoerce方法,生成對象若是是個thenable對象調用對象的then方法不然直接onResolve方法。

PromiseCoerce

function 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,則調用then方法。如有報錯走到reject方法。

PromiseChain

PromiseChain = function PromiseChain(onResolve, onReject) {  
    // 補默認的處理函數
    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實現的核心,判斷當前定義的promise狀態,若是是pending狀態在promiseOnResolve數組中push當前的onResolve方法。若是是Resolved狀態或者Rejected狀態,則調用PromiseEnqueue函數進行微任務的添加。

PromiseEnqueue

function 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函數加入JS運行時的微任務隊列中。微任務的隊列列表是C語言進行維護的,應用%EnqueueMicrotask方法進行添加。

PromiseHandle

function 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();
    }
}

此函數處理了傳入的方法,是指是調用了resolve方法,若是返回的結果依舊是一個promise則繼續調用PromiseChain方法,不然調用新生成的promise實例的resolve方法,進而實現循壞調用。

promise.all

promise的all方法實現了發送多個promise請求,返回一個新的promise,全部promise打到resolve狀態時觸發resolve狀態,如有一個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執行then方法後湊執行判斷邏輯,當計數count的flag爲0的時候,全部promise resolve完成,調用新promise對象的resolve方法,傳入新promise的reject方法做爲then方法reject參數。使得全部promise的reject函數被調用時都會走到新promise對象的reject,最後返回新生成的promise。

Summary

Promise的狀態和核心變量均託管到公共的做用域去維護,經過數組的push方法去添加Promise自定的resolve和reject方法。並將resolve和reject方法的執行加入微服務隊列中,等到resolve方法被調用時執行resolve(value)方法進行調用。爲了實現promise的循環嵌套調用,在每次處理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();
            // 進入宏任務隊列
            enqueue(function () {
                result.resolve(callback(value));
            });
            return result.promise;
        }
    };
};

Reference

promise設計思想:https://github.com/kriskowal/...

JavaScript執行機制:https://www.jianshu.com/p/17c...

相關文章
相關標籤/搜索