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