React Native开发

JavaScriptCore源码探析-JS运行时(二)-Prom

2020-05-30  本文已影响0人  FingerStyle

Promise

首先,我们看下JS里面promise的实现,注意这里带有@符号前缀的方法有部分是由对应的C++方法实现的,可以在工程里根据方法名字搜索
先看下构造方法

@globalPrivate
function initializePromise(executor)
{
    "use strict";

    if (typeof executor !== 'function')
        @throwTypeError("Promise constructor takes a function argument");
    //设置promiseState为pending
    @putByIdDirectPrivate(this, "promiseState", @promiseStatePending);
    //初始化promiseReactions数组
    @putByIdDirectPrivate(this, "promiseReactions", []);
    @putByIdDirectPrivate(this, "promiseIsHandled", false);
   //创建解决函数,也就是resolve和reject
    var resolvingFunctions = @createResolvingFunctions(this);
    try {
        executor(resolvingFunctions.@resolve, resolvingFunctions.@reject);
    } catch (error) {
        return resolvingFunctions.@reject.@call(@undefined, error);
    }

    return this;
}

@globalPrivate
function createResolvingFunctions(promise)
{
    "use strict";

    var alreadyResolved = false;

    function @resolve(resolution) {
        if (alreadyResolved)
            return @undefined;
        alreadyResolved = true;
       //如果resolve的函数就是promise本身,则报错
        if (resolution === promise)
            return @rejectPromise(promise, new @TypeError("Resolve a promise with itself"));

        if (!@isObject(resolution))
            return @fulfillPromise(promise, resolution);
        //尝试获取then
        var then;
        try {
            then = resolution.then;
        } catch (error) {
            //出现错误,则调用reject
            return @rejectPromise(promise, error);
        }

        if (typeof then !== 'function')
            return @fulfillPromise(promise, resolution);
        //获取成功的话,往任务队列中追加一个promiseResolveThenableJob任务
        @enqueueJob(@promiseResolveThenableJob, [promise, resolution, then]);

        return @undefined;
    }

    function @reject(reason) {
        if (alreadyResolved)
            return @undefined;
        alreadyResolved = true;

        return @rejectPromise(promise, reason);
    }

    return { @resolve, @reject };
}

@globalPrivate
//这个任务的作用就是调用promise的then函数,并捕获异常
function promiseResolveThenableJob(promiseToResolve, thenable, then)
{
    "use strict";

    var resolvingFunctions = @createResolvingFunctions(promiseToResolve);

    try {
        return then.@call(thenable, resolvingFunctions.@resolve, resolvingFunctions.@reject);
    } catch (error) {
        return resolvingFunctions.@reject.@call(@undefined, error);
    }
}

再看下fullfill和reject

@globalPrivate
function triggerPromiseReactions(state, reactions, argument)
{
    "use strict";
    //把所有的reactions(resolve和reject的组合)取出来放入队列执行
    for (var index = 0, length = reactions.length; index < length; ++index)
        @enqueueJob(@promiseReactionJob, [state, reactions[index], argument]);
}

@globalPrivate
function rejectPromise(promise, reason)
{
    "use strict";

    var reactions = @getByIdDirectPrivate(promise, "promiseReactions");
    @putByIdDirectPrivate(promise, "promiseResult", reason);
    @putByIdDirectPrivate(promise, "promiseReactions", @undefined);
    @putByIdDirectPrivate(promise, "promiseState", @promiseStateRejected);
    //这里引入了inspector,对promise做性能检测
    @InspectorInstrumentation.promiseRejected(promise, reason, reactions);

    if (!@getByIdDirectPrivate(promise, "promiseIsHandled"))
        @hostPromiseRejectionTracker(promise, @promiseRejectionReject);

    @triggerPromiseReactions(@promiseStateRejected, reactions, reason);
}

@globalPrivate
function fulfillPromise(promise, value)
{
    "use strict";

    var reactions = @getByIdDirectPrivate(promise, "promiseReactions");
    @putByIdDirectPrivate(promise, "promiseResult", value);
    @putByIdDirectPrivate(promise, "promiseReactions", @undefined);
    @putByIdDirectPrivate(promise, "promiseState", @promiseStateFulfilled);
    //这里引入了inspector,对promise做性能检测
    @InspectorInstrumentation.promiseFulfilled(promise, value, reactions);

    @triggerPromiseReactions(@promiseStateFulfilled, reactions, value);
}

在PromiseProtoType.js里面我们可以看到 then方法的具体实现

function then(onFulfilled, onRejected)
{
    "use strict";

    if (!@isPromise(this))
        @throwTypeError("|this| is not a object");

    var constructor = @speciesConstructor(this, @Promise);

    var resultCapability = @newPromiseCapability(constructor);

    if (typeof onFulfilled !== "function")
        onFulfilled = function (argument) { return argument; };

    if (typeof onRejected !== "function")
        onRejected = function (argument) { throw argument; };
    //把resolve和reject包装一下
    var reaction = @newPromiseReaction(resultCapability, onFulfilled, onRejected);

    var state = @getByIdDirectPrivate(this, "promiseState");
     //如果还是处于pending状态,则把新创建的reaction加入到reactions里面
    if (state === @promiseStatePending) {
        var reactions = @getByIdDirectPrivate(this, "promiseReactions");
        @putByValDirect(reactions, reactions.length, reaction);
    } else {//其他状态,rejected或者fullfilled
        if (state === @promiseStateRejected && !@getByIdDirectPrivate(this, "promiseIsHandled"))
            @hostPromiseRejectionTracker(this, @promiseRejectionHandle);
        //这时候Promise本身的任务已经执行完了,调用then则直接把任务加到队列里执行
        @enqueueJob(@promiseReactionJob, [state, reaction, @getByIdDirectPrivate(this, "promiseResult")]);
    }

    @putByIdDirectPrivate(this, "promiseIsHandled", true);

    return resultCapability.@promise;
}

Promise.then()调用了enqueueJob ,而enqueueJob实际则先创建了一个microTask,然后调用了queueMicroTask加到任务队列里面。

static EncodedJSValue JSC_HOST_CALL enqueueJob(ExecState* exec)
{
    VM& vm = exec->vm();
    JSGlobalObject* globalObject = exec->lexicalGlobalObject();

    JSValue job = exec->argument(0);
    JSValue arguments = exec->argument(1);
    ASSERT(arguments.inherits<JSArray>(vm));

    globalObject->queueMicrotask(createJSJob(vm, job, jsCast<JSArray*>(arguments)));

    return JSValue::encode(jsUndefined());
}

MicroTask

这里解释下MicroTask
MicroTask是一个抽象类,定义了run接口,用于执行一小段任务。

而JSJobMicroTask是他的具体实现,继承自microTask,实现了run接口

class JSJobMicrotask final : public Microtask {
public:
    JSJobMicrotask(VM& vm, JSValue job, JSArray* arguments)
    {
        m_job.set(vm, job);
        m_arguments.set(vm, arguments);
    }

    virtual ~JSJobMicrotask()
    {
    }

private:
    void run(ExecState*) override;

    Strong<Unknown> m_job;
    Strong<JSArray> m_arguments;
};

Ref<Microtask> createJSJob(VM& vm, JSValue job, JSArray* arguments)
{
    return adoptRef(*new JSJobMicrotask(vm, job, arguments));
}

void JSJobMicrotask::run(ExecState* exec)
{
    VM& vm = exec->vm();
    //获取作用域
    auto scope = DECLARE_CATCH_SCOPE(vm);

    CallData handlerCallData;
    CallType handlerCallType = getCallData(vm, m_job.get(), handlerCallData);
    ASSERT(handlerCallType != CallType::None);
   //检查参数合法性
    MarkedArgumentBuffer handlerArguments;
    for (unsigned index = 0, length = m_arguments->length(); index < length; ++index) {
        JSValue arg = m_arguments->JSArray::get(exec, index);
        CLEAR_AND_RETURN_IF_EXCEPTION(scope, handlerArguments.overflowCheckNotNeeded());
        handlerArguments.append(arg);
    }
    if (UNLIKELY(handlerArguments.hasOverflowed()))
        return;
    //执行调用
    profiledCall(exec, ProfilingReason::Microtask, m_job.get(), handlerCallType, handlerCallData, jsUndefined(), handlerArguments);
    scope.clearException();
}

}

run的作用: 获取scope,检查参数合法性,调用profiledCall执行任务
run在drainMicroTask中被调用。

MicroTaskQueue

任务队列,是VM的一个成员变量 ,名称叫m_microTaskQueue, 相关的方法有两个,一个是queueMicroTask,一个是drainMicroTask.

void VM::queueMicrotask(JSGlobalObject& globalObject, Ref<Microtask>&& task)
{ 
   //把任务加入队列末尾
    m_microtaskQueue.append(std::make_unique<QueuedTask>(*this, &globalObject, WTFMove(task)));
}
void VM::drainMicrotasks()
{
  //把队列中所有任务取出来执行(每次从队列头部取出任务)
    while (!m_microtaskQueue.isEmpty())
        m_microtaskQueue.takeFirst()->run();
}

drainMicroTask调用的地方有几个:

  1. jsc.cpp 里面,比如runWithOptions、runInteractive、runJSC,调用jsCore时执行。
  2. promiseDeferredTimer的doWork函数里面,执行某个延时操作时调用

jsc.cpp

jsc.cpp是javascriptCore工程的main文件,即程序启动的入口文件。调用runJSC就是开始运行javascriptCore。runJSC里面开启了runloop,在mac os和ios上就是CFRunloop,其他平台上也是类似的运行循环。在runJSC里面还启动了一个PromiseDeferredTimer,用来处理延迟操作

template<typename Func>
int runJSC(CommandLine options, bool isWorker, const Func& func)
{
    Worker worker(Workers::singleton());
    
    VM& vm = VM::create(LargeHeap).leakRef();
    int result;
    bool success = true;
    GlobalObject* globalObject = nullptr;
    {
        JSLockHolder locker(vm);

        if (options.m_profile && !vm.m_perBytecodeProfiler)
            vm.m_perBytecodeProfiler = std::make_unique<Profiler::Database>(vm);

        globalObject = GlobalObject::create(vm, GlobalObject::createStructure(vm, jsNull()), options.m_arguments);
        globalObject->setRemoteDebuggingEnabled(options.m_enableRemoteDebugging);
        func(vm, globalObject, success);
        //取出队列中的所有任务并执行
        vm.drainMicrotasks();
    }
    //开启runloop,执行定时任务
    vm.promiseDeferredTimer->runRunLoop();
    {
        JSLockHolder locker(vm);
        if (options.m_interactive && success)
            runInteractive(globalObject);
    }
   //......
}

JSRunLoopTimer

JScore利用CFRunloop的timer执行定时操作,跟iOS的NSTimer类似,有scheduleTimer、cancelTimer等方法。内部持有了CFRunLoopTimerRef类型的一个对象m_timer及CFRunloopRef类型的m_runLoop。
promiseDeferredTimer就是继承于JSRunLoopTimer.

class PromiseDeferredTimer : public JSRunLoopTimer {
public:
    using Base = JSRunLoopTimer;

    PromiseDeferredTimer(VM&);

    void doWork() override;

    void addPendingPromise(JSPromiseDeferred*, Vector<Strong<JSCell>>&& dependencies);
    JS_EXPORT_PRIVATE bool hasPendingPromise(JSPromiseDeferred* ticket);
    JS_EXPORT_PRIVATE bool hasDependancyInPendingPromise(JSPromiseDeferred* ticket, JSCell* dependency);
    // JSPromiseDeferred should handle canceling when the promise is resolved or rejected.
    bool cancelPendingPromise(JSPromiseDeferred*);

    typedef std::function<void()> Task;
    void scheduleWorkSoon(JSPromiseDeferred*, Task&&);

    void stopRunningTasks() { m_runTasks = false; }

    JS_EXPORT_PRIVATE void runRunLoop();

private:
    HashMap<JSPromiseDeferred*, Vector<Strong<JSCell>>> m_pendingPromises;
    Lock m_taskLock;
    bool m_runTasks { true };
    bool m_shouldStopRunLoopWhenAllPromisesFinish { false };
    bool m_currentlyRunningTask { false };
    Vector<std::tuple<JSPromiseDeferred*, Task>> m_tasks;
};

} // namespace JSC

promiseDeferredTimer的doWork是在timerDidFire函数中调用的。用来执行具体的任务,可以看到,这里为了避免多线程同时执行任务,还做了加锁操作,这也提现了javascript单线程的特点。

void PromiseDeferredTimer::doWork()
{
    ASSERT(m_vm->currentThreadIsHoldingAPILock());
    m_taskLock.lock();
    cancelTimer();
    if (!m_runTasks) {
        m_taskLock.unlock();
        return;
    }

    while (!m_tasks.isEmpty()) {
        JSPromiseDeferred* ticket;
        Task task;
        std::tie(ticket, task) = m_tasks.takeLast();
        dataLogLnIf(PromiseDeferredTimerInternal::verbose, "Doing work on promise: ", RawPointer(ticket));

        // We may have already canceled these promises.
        if (m_pendingPromises.contains(ticket)) {
            // Allow tasks we run now to schedule work.
            m_currentlyRunningTask = true;
            m_taskLock.unlock(); 

            task();
            m_vm->drainMicrotasks();

            m_taskLock.lock();
            m_currentlyRunningTask = false;
        }
    }

    if (m_pendingPromises.isEmpty() && m_shouldStopRunLoopWhenAllPromisesFinish) {
#if USE(CF)
        CFRunLoopStop(m_runLoop.get());
#else
        RunLoop::current().stop();
#endif
    }

    m_taskLock.unlock();
}

同样继承自JSRunLoopTimer的还有StopIfNecessaryTimer、IncrementalSweeper和GCActivityCallback,这些都是跟JS内存管理相关的类,这里就不展开讲了,后续会专门开一篇讲垃圾回收的实现。

VM中有一个m_runLoopTimers数组维护JSRunLoopTimer。每次调用registerRunLoopTimer就会往m_runLoopTimers中添加一个JSRunLoopTimer。

void VM::registerRunLoopTimer(JSRunLoopTimer* timer)
{
    ASSERT(runLoop());
    ASSERT(!m_runLoopTimers.contains(timer));
    m_runLoopTimers.add(timer);
    timer->setRunLoop(runLoop());
}

void VM::unregisterRunLoopTimer(JSRunLoopTimer* timer)
{
    ASSERT(m_runLoopTimers.contains(timer));
    m_runLoopTimers.remove(timer);
    timer->setRunLoop(nullptr);
}

这里runloop()是获取当前线程的runloop
我们知道,iOS的runloop是跟线程一一对应的,那么JS的runloop既然是使用了CFRunloop,那也是跟线程一一对应,那么也就不难理解JS里面的任务为什么是串行的了,因为VM里面的runloop只有一个,只能在一个事件循环中按顺序执行。对于Promise是如此,对于其他的异步任务,如Timer、async/await等也是如此。

上一篇下一篇

猜你喜欢

热点阅读