事件循环:微任务和宏任务在v8中实现的简单理解

微任务

在js中,当使用promise,会将当前任务加入事件执行的微任务队列,有且只有这一种方法可以,因为当使用了promise,在JS引擎中会触发VM::queueMicrotask,会向m_microtaskQueue队列中压入事件,在V8中只有这一种暴露方式,没有其他接口可以调用这个方法

void VM::queueMicrotask(JSGlobalObject& globalObject, Ref<Microtask>&& task)
{
    m_microtaskQueue.append(makeUnique<QueuedTask>(*this, &globalObject, WTFMove(task)));
}

然后会执行VM::drainMicrotask,只要m_microtaskQueue不为空就会一直取微任务队列第一个去执行

void VM::drainMicrotasks()
{
    do {
        while (!m_microtaskQueue.isEmpty()) {
            m_microtaskQueue.takeFirst()->run();
            if (m_onEachMicrotaskTick)
                m_onEachMicrotaskTick(*this);
        }
        didExhaustMicrotaskQueue();
    } while (!m_microtaskQueue.isEmpty());
    finalizeSynchronousJSExecution();
}

所以Promise会将事件放到微任务队列的最后一位,然后继续执行,如果promise嵌套,内层的promise也会被放到微任务队列的最末尾,然后执行的时候再将其中的任务放到队列的末尾

宏任务

但是相比于微任务是js-core中自带的基础设施,js-core源代码中是不包含宏任务相关内容的,对于宏任务的相关内容是在C++的js引擎中实现的,单个js代码是通过语句是通过evaluateScript进行执行,而宏任务的实现相当于在C++中通过循环来去执行多个evaluateScript方法,而每个evaluateScript在执行前都有lock保证队列已经清空
下面是宏任务实现的伪代码

// 初始化JS的执行环境
JSContext* context = [[JSContext alloc] init];
// 初始化C++环境的宏任务队列
NSMutableArray * macroTaskQueue = [[NSMutableArray alloc] init];
// 创建微任务锁
NSConditionLock* condLock = [[NSConditionLock alloc] initWithCondition:0];

// 引入js中的console.log方法,并转换为NSlog输出
context[@"console"] = [JSValue valueWithNewObjectInContext:context];
context[@"console"][@"log"] = ^(JSValue* s) {
    NSLog(@"%@", [s toString]);
};

// 引入setTimeout方法
context[@"setTimeout"] = ^(JSValue* f, JSValue* duration) {
    NSThread* timer = [[NSThread alloc]initWithBlock:^{
        [NSThread sleepForTimeInterval:[duration toDouble] / 1000]; // 休眠1秒
        
        [condLock lock]; // 使用锁
        
        [macroTaskQueue addObject:f]; // 将任务的压入引擎宏任务队列
        
        [condLock unlockWithCondition:1]; 将微任务锁设置为1
        //[f callWithArguments:@[]];
    }];
    [timer start];
};

NSThread* scanner = [[NSThread alloc]initWithBlock:^{
    char sourceCode[1024];
    while(scanf("%[^\n]", sourceCode) != -1) {
        getchar();
        [condLock lock];
        
        [macroTaskQueue addObject:[NSString stringWithUTF8String: sourceCode]];
        
        [condLock unlockWithCondition:1];
    }
}];
[scanner start];

while(true) { //Event Loop
    [condLock lockWhenCondition:1]; // 将锁设置位1
    for (id task in macroTaskQueue){
        if([task isKindOfClass:JSValue.class]) 
            [task callWithArguments:@[]];
        if([task isKindOfClass:NSString.class]) {
            JSValue* result = [context evaluateScript:task]; // 执行js内容
            NSLog(@"%@", [result toString]);
        }
    }
    [macroTaskQueue removeAllObjects]; // 将宏任务队列清空
    [condLock unlockWithCondition:0]; // 将锁设置位0
}
posted @ 2022-08-20 09:30  chenSee  阅读(191)  评论(0编辑  收藏  举报