美文网首页React Native开发
JavaScriptCore源码探析-JS运行时(二)-Prom

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

作者: FingerStyle | 来源:发表于2020-05-30 11:20 被阅读0次

    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等也是如此。

    相关文章

      网友评论

        本文标题:JavaScriptCore源码探析-JS运行时(二)-Prom

        本文链接:https://www.haomeiwen.com/subject/scyczhtx.html