从Google-V8引擎剖析Promise实现

2次阅读

共计 8379 个字符,预计需要花费 21 分钟才能阅读完成。

从 Google V8 引擎剖析 Promise 实现

​ 本文阅读的源码为 Google V8 Engine v3.29.45,此版本的 promise 实现为 js 版本,在后续版本 Google 继续对其实现进行了处理。引入了 es6 语法等,在 7.X 版本迭代后,逐渐迭代成了 C 版本实现。

​ 贴上源码地址:https://chromium.googlesource… 大家自觉传送。

​ 代码中所有类似 %functionName 的函数均是 C 语言实现的运行时函数。

Define variables

首先定义了将要在 JS 作用域使用了一些变量,提高了编译器的效率。

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

随后定义了一些全局私有变量供给和 C 语音交互,用于维护 Promise 的状态和进行 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 进行实现的,运用 python 的宏定义(macro)来定义调用了 C 语言的 CreateGlobalPrivateOwnSymbol 方法。

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

随后运用了一个自执行的匿名函数进行闭包逻辑。

(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 方法。

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…

正文完
 0