我们从setImmediate开始分析,
function setImmediate(callback, arg1, arg2, arg3) {
if (typeof callback !== 'function') {
throw new ERR_INVALID_CALLBACK(callback);
}
let i, args;
switch (arguments.length) {
case 1:
break;
case 2:
args = [arg1];
break;
case 3:
args = [arg1, arg2];
break;
default:
args = [arg1, arg2, arg3];
for (i = 4; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
break;
}
return new Immediate(callback, args);
}
setImmediate的代码比较简单,新建一个Immediate。我们看一下Immediate的类。
const Immediate = class Immediate {
constructor(callback, args) {
this._idleNext = null;
this._idlePrev = null;
this._onImmediate = callback;
this._argv = args;
this._destroyed = false;
this[kRefed] = false;
initAsyncResource(this, 'Immediate');
this.ref();
// Immediate链表的节点个数,包括ref和unref状态
immediateInfo[kCount]++;
// 加入链表中
immediateQueue.append(this);
}
// 打上ref标记,往libuv的idle链表插入一个节点,如果还没有的话
ref() {
if (this[kRefed] === false) {
this[kRefed] = true;
if (immediateInfo[kRefCount]++ === 0)
toggleImmediateRef(true);
}
return this;
}
// 和上面相反
unref() {
if (this[kRefed] === true) {
this[kRefed] = false;
if (--immediateInfo[kRefCount] === 0)
toggleImmediateRef(false);
}
return this;
}
hasRef() {
return !!this[kRefed];
}
};
Immediate类主要做了两个事情。 1 生成一个节点插入到链表。
const immediateQueue = new ImmediateList();
// 双向非循环的链表
function ImmediateList() {
this.head = null;
this.tail = null;
}
// Appends an item to the end of the linked list, adjusting the current tail's
// previous and next pointers where applicable
ImmediateList.prototype.append = function(item) {
// 尾指针非空,说明链表非空,直接追加在尾节点后面
if (this.tail !== null) {
this.tail._idleNext = item;
item._idlePrev = this.tail;
} else {
// 尾指针是空说明链表是空的,头尾指针都指向item
this.head = item;
}
this.tail = item;
};
// Removes an item from the linked list, adjusting the pointers of adjacent
// items and the linked list's head or tail pointers as necessary
ImmediateList.prototype.remove = function(item) {
// 如果item在中间则自己全身而退,前后两个节点连上
if (item._idleNext !== null) {
item._idleNext._idlePrev = item._idlePrev;
}
if (item._idlePrev !== null) {
item._idlePrev._idleNext = item._idleNext;
}
// 是头指针,则需要更新头指针指向item的下一个,因为item被删除了,尾指针同理
if (item === this.head)
this.head = item._idleNext;
if (item === this.tail)
this.tail = item._idlePrev;
// 重置前后指针
item._idleNext = null;
item._idlePrev = null;
};
2 然后如果还没有往libuv的idle链表里插入节点的话,则插入一个。
void ToggleImmediateRef(const FunctionCallbackInfo<Value>& args) {
Environment::GetCurrent(args)->ToggleImmediateRef(args[0]->IsTrue());
}
void Environment::ToggleImmediateRef(bool ref) {
if (started_cleanup_) return;
// 往idle链表插入/删除一个节点,插入节点是防止在poll io阶段阻塞
if (ref) {
// Idle handle is needed only to stop the event loop from blocking in poll.
uv_idle_start(immediate_idle_handle(), [](uv_idle_t*){ });
} else {
uv_idle_stop(immediate_idle_handle());
}
}
这是setImmediate函数的整个过程,他是一个生产者。我们来看一下消费者。nodejs在初始化的时候,会在check阶段插入一个节点,并注册一个回调。
uv_check_start(immediate_check_handle(), CheckImmediate);
void Environment::CheckImmediate(uv_check_t* handle) {
// 省略部分代码
// 没有Immediate节点需要处理
if (env->immediate_info()->count() == 0 || !env->can_call_into_js())
return;
do {
// 执行js层回调immediate_callback_function
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());
// 所有的immediate节点都处理完了,删除idle链表的那个节点,即允许poll io阶段阻塞
if (env->immediate_info()->ref_count() == 0)
env->ToggleImmediateRef(false);
}
CheckImmediate函数会在libuv的check阶段被执行。然后他执行immediate_callback_function函数处理immediate链表的节点。我们看一下immediate_callback_function函数是在哪设置的。
const { setupTimers } = internalBinding('timers')
setupTimers(processImmediate, processTimers);
// js层setupTimers =》 c++层 SetupTimers
void SetupTimers(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsFunction());
CHECK(args[1]->IsFunction());
auto env = Environment::GetCurrent(args);
env->set_immediate_callback_function(args[0].As<Function>());
env->set_timers_callback_function(args[1].As<Function>());
}
所以processImmediate函数就是真正处理immediate链表的函数。
const immediateQueue = new ImmediateList();
const outstandingQueue = new ImmediateList()
function processImmediate() {
// 上次执行processImmediate的时候有遗留的节点则执行outstandingQueue队列,这时候immediateQueue队列是空的
const queue = outstandingQueue.head !== null ?
outstandingQueue : immediateQueue;
let immediate = queue.head;
/*
在执行immediateQueue队列的话,先置空队列,避免执行回调的时候一直往队列加节点,死循环。
所以新加的接口会插入新的队列,不会在本次被执行。
并打一个标记,全部immediateQueue节点都被执行则清空,否则会再执行processImmediate一次,见Environment::CheckImmediate
*/
if (queue !== outstandingQueue) {
queue.head = queue.tail = null;
immediateInfo[kHasOutstanding] = 1;
}
let prevImmediate;
let ranAtLeastOneImmediate = false;
while (immediate !== null) {
// 执行宏任务
if (ranAtLeastOneImmediate)
runNextTicks();
else
ranAtLeastOneImmediate = true;
// 宏任务把该节点删除了,则不需要指向他的回调了,继续下一个
if (immediate._destroyed) {
outstandingQueue.head = immediate = prevImmediate._idleNext;
continue;
}
immediate._destroyed = true;
// 执行完要修改个数
immediateInfo[kCount]--;
if (immediate[kRefed])
immediateInfo[kRefCount]--;
immediate[kRefed] = null;
// 见上面if (immediate._destroyed)的注释
prevImmediate = immediate;
// 执行回调,指向下一个节点
try {
const argv = immediate._argv;
if (!argv)
immediate._onImmediate();
else
immediate._onImmediate(...argv);
} finally {
immediate._onImmediate = null;
if (destroyHooksExist())
emitDestroy(asyncId);
outstandingQueue.head = immediate = immediate._idleNext;
}
}
// 当前执行的是outstandingQueue的话则把他清空
if (queue === outstandingQueue)
outstandingQueue.head = null;
// 全部节点执行完
immediateInfo[kHasOutstanding] = 0;
}
这就是setImmediate的原理。