任务注册
setupTaskQueue
{nextTick, runNextTicks} internal/process/task_queues 暴露两个方法给nodejs的process用
// MARK: nodejs自己用js实现的一个 队列, 用来存放nextTick回调函数
const queue = new FixedQueue();
// ...
// 插入nextTick回调函数
function nextTick(callback) {
// 先设置标识 为 true: 表示需要忘 c++ 中的tak_queue中的tickInfo[0] 首位插入 1
if (queue.isEmpty()) setHasTickScheduled(true);
// 生成异步ID
const asyncId = newAsyncId();
// 任务触发ID
const triggerAsyncId = getDefaultTriggerAsyncId();
// tick
const tickObject = {
[async_id_symbol]: asyncId,
[trigger_async_id_symbol]: triggerAsyncId,
callback,
args
};
// 初始化成功
if (initHooksExist())
// 发出事件
emitInit(asyncId, 'TickObject', triggerAsyncId, tickObject);
// 队列中添加
queue.push(tickObject);
}
// 运行nextTick
function runNextTicks() {
// 如果没有 nextTick 或者 promiseRejection 则只运行 runMicrotasks 函数, 否则运行 processTicksAndRejections 函数。
if (!hasTickScheduled() && !hasRejectionToWarn())
runMicrotasks();
if (!hasTickScheduled() && !hasRejectionToWarn())
return;
processTicksAndRejections();
}
// 真的运行nextTick
function processTicksAndRejections() {
// 如果queue中有nextTick, 就将queque中的callback一个一个地拿出来运行
// 最后清空 相关字段
if (destroyHooksExist()) emitDestroy(asyncId);
// 运行结束后的钩子
emitAfter(asyncId);
// 如果有promise.then和reject的微任务(processPromiseRejections),
runMicrotasks();
// 最后,将微任务列表状态设置为 false
setHasTickScheduled(false);
setHasRejectionToWarn(false);
}
getTimerCallbacks
internal/timers
// 启动的时候在lib/internal/timers中创建单链:
const immediateQueue = new ImmediateList();
// 创建一个堆,根据时间和创建先后排序
// PriorityQueue——一个高效的二进制堆实现,在最坏情况下执行所有操作的时间为O(log n)
const timerListQueue = new PriorityQueue(compareTimersLists, setPosition);
// 用来缓存,timer数据
// key = 毫秒级的时间 value为链表
const timerListMap = ObjectCreate(null);
// 暴露的方法会执行nextTicks列表以及微任务 (闭包)
function getTimerCallbacks(runNextTicks) {
// processImmediate的逻辑就是逐个执行immediate任务队列的节点。Immediate分两个队列,正常情况下,插入的immediate节点插入到immediateQueue队列
// 如果执行的时候有异常,则未处理完的节点就会被插入到outstandingQueue队列,等下一次循环时先执行
function processImmediate() {
const queue = outstandingQueue.head !== null ?
outstandingQueue : immediateQueue;
const immediate = queue.head
let prevImmediate;
let ranAtLeastOneImmediate = false;
// 开始逐个执行任务 先执行一个_onImmediate,而后每次循环前,先执行 微任务和 nextTick
while(immediate !== null) {
// 执行微任务队列
if (ranAtLeastOneImmediate) runNextTicks();
else ranAtLeastOneImmediate = true;
if (immediate._destroyed) {
outstandingQueue.head = immediate = prevImmediate._idleNext;
continue;
}
// 执行下一个节点
outstandingQueue.head = immediate = immediate._idleNext;
}
}
// 处理计时器
function processTimers(now) {
let list;
let ranAtLeastOneList = false;
// 逐个处理已经过期的计时器任务,以及每次循环前,先执行nextTicks和v8微任务
while(list = timerListQueue.peek()) {
if (list.expiry > now) {
nextExpiry = list.expiry;
return refCount > 0 ? nextExpiry : -nextExpiry;
}
// 第二次循环往后,执行微任务
if (ranAtLeastOneList) runNextTicks();
else ranAtLeastOneList = true;
listOnTimeout(list, now);
}
return 0;
}
function listOnTimeout(list, now) {
while (timer = L.peek(list)) {
if (ranAtLeastOneTimer) runNextTicks();
else ranAtLeastOneTimer = true;
// 当超时,移除任务
L.remove(timer);
// 如果是setInterval
if (timer._repeat) start = getLibuvNow();
// 先执行当前timer
timer._onTimeout()
// 如果是setInterval这种repeat任务,需要重新insert, 否则结束
timer._idleTimeout = timer._repeat;
// 所有定时器缓存在timerListMap中,每个迭代时间都有一个列表,每个时间间隔区间的任务存储在一个单独的list,如果没有就需要单独创建, 所有这个任务链表放在一个mini heap结构中
// timer._idleTimeout,会被截断,保证亚毫秒级的准确性
// 如:msecs = MathTrunc(msecs);
insert(timer, timer._idleTimeout, start);
// 否则结束
timer._destroyed = true;
}
}
return {
processImmediate,
processTimers
};
}
lib/internal/bootstrap/node.js
const { processImmediate, processTimers } = getTimerCallbacks(runNextTicks);
const { setupTimers } = internalBinding('timers');
setupTimers(processImmediate, processTimers);
setupTimers
src/timers.cc
void SetupTimers(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsFunction());
CHECK(args[1]->IsFunction());
auto env = Environment::GetCurrent(args);
// 在nodejs启动的时候就将各个任务注册到nodejs
env->set_immediate_callback_function(args[0].As<Function>());
env->set_timers_callback_function(args[1].As<Function>());
}
env.h
定义了一个带参宏V,而这个宏在调用的时候会定义一个属性,所以在通过V(immediate_callback_function, v8::Function) 调用后,可以实现env->set_immediate_callback_function的调用了,同时还会生成一个成员函数PropertyName(),所以在调用后同时也会使得env->immediate_callback_function成为可调用函数。
args[0]在这里指的便是processImmediate,所以通过SetupTimers可以使processImmediate这个函数最终注册到node中。
V(immediate_callback_function, v8::Function)
#define V(TypeName, PropertyName)
inline v8::Local<TypeName> PropertyName() const;
PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(VP)
PER_ISOLATE_SYMBOL_PROPERTIES(VY)
PER_ISOLATE_STRING_PROPERTIES(VS)
#undef V
新增/删除异步任务
lib/timers.js
setImmediate / clearImmediate
setImmediate会新建一个节点,并把节点append到immediateQueue,并返回当前节点
clearImmediate会从immediateQueue删除指定的节点
function setImmediate() {
// ...
return new Immediate(callback, args);
}
function clearImmediate(immediate) {
// ...
immediateQueue.remove(immediate);
}
setTimeout / clearTimeout
setTimeout会新建一个timeout节点,然后执行insert,在insert操作中,会先判断timerListMap是否有该msecs毫米级的链表,有就append,没有就先创建TimersList,然后append,在存入堆:timerListQueue
clearTimeout执行删除操作,删除msecs级别的任务,然后看该链表是否已空,空就在timerListQueue和timerListMap中删除
function setTimeout() {
// ...
const timeout = new Timeout(callback, after, args, false, true);
insert(timeout, timeout._idleTimeout);
}
function clearTimeout(timer) {
// ...
unenroll(immediate);
}
setInterval / clearInterval
setInterval会先按照setImmediate的方式,插入repeat属性的节点,然后在event loop时,会检查该属性,然后再新建一个任务,insert到指定的链表中
clearInterval删除节点,基本同clearTimeout
function setInterval() {
// ...
const timeout = new Timeout(callback, repeat, args, false, true);
insert(timeout, timeout._idleTimeout);
}
function clearInterval(immediate) {
// ...
unenroll(immediate);
}
任务执行
src/env.cc
InitializeLibuv
// TODO:
uv_check_start(immediate_check_handle(), CheckImmediate);
// 这里执行的是uv的官方的⌚️
uv_async_init(
event_loop(),
// task_queues_async_用于子线程和主线程通信
&task_queues_async_,
// uv_async_cb
[](uv_async_t* async) {
Environment* env = ContainerOf( &Environment::task_queues_async_, async);
env->RunAndClearNativeImmediates();
}
);
CheckImmediate
src/env.cc
do {
// MakeCallback来自src/api/callbacl.cc
MakeCallback(env->isolate(),
env->process_object(),
env->immediate_callback_function(),
0,
nullptr,
{0, 0}).ToLocalChecked();
} while (env->immediate_info()->has_outstanding() && env->can_call_into_js());