我一直在深入了解 Node.js 架构的内部结构,我经常看到的一个术语是“tick”,如“事件循环的下一个滴答声”或函数nextTick()。
我还没有看到对“tick”到底是什么的明确定义。根据各种文章(例如这篇文章),我已经能够在脑海中拼凑出一个概念,但我不确定它有多准确。
我可以得到一个 Node.js 事件循环刻度的准确和详细的描述吗?
我一直在深入了解 Node.js 架构的内部结构,我经常看到的一个术语是“tick”,如“事件循环的下一个滴答声”或函数nextTick()。
我还没有看到对“tick”到底是什么的明确定义。根据各种文章(例如这篇文章),我已经能够在脑海中拼凑出一个概念,但我不确定它有多准确。
我可以得到一个 Node.js 事件循环刻度的准确和详细的描述吗?
请记住,虽然 JavaScript 是单线程的,但所有节点的 I/O 和对本机 API 的调用要么是异步的(使用特定于平台的机制),要么在单独的线程上运行。(这都是通过 libuv 处理的。)
因此,当套接字上有可用数据或本机 API 函数返回时,我们需要一种同步方式来调用对刚刚发生的特定事件感兴趣的 JavaScript 函数。
仅从发生本机事件的线程调用 JS 函数是不安全的,原因与您在常规多线程应用程序中遇到的原因相同——竞争条件、非原子内存访问等。
所以我们所做的是以线程安全的方式将事件放在队列中。在过于简化的伪代码中,类似于:
lock (queue) {
queue.push(event);
}
然后,回到主 JavaScript线程(但在 C 方面),我们执行以下操作:
while (true) {
// this is the beginning of a tick
lock (queue) {
var tickEvents = copy(queue); // copy the current queue items into thread-local memory
queue.empty(); // ..and empty out the shared queue
}
for (var i = 0; i < tickEvents.length; i++) {
InvokeJSFunction(tickEvents[i]);
}
// this the end of the tick
}
(while (true)
实际上并不存在于节点的源代码中;这纯粹是说明性的)代表事件循环。内部for
为队列中的每个事件调用 JS 函数。
这是一个滴答声:同步调用与任何外部事件相关的零个或多个回调函数。一旦队列被清空并且最后一个函数返回,tick 就结束了。我们回到开始(下一个滴答声)并检查在我们的 JavaScript 运行时从其他线程添加到队列中的事件。
什么可以添加到队列中?
process.nextTick
setTimeout
/setInterval
fs
、net
等的东西)crypto
的处理器密集型功能,例如加密流、pbkdf2 和 PRNG(实际上是...的一个示例)对于 JavaScript 新手来说,一个更简单的答案:
首先要理解的是,JavaScript 是一个“单线程环境”。这是指 JavaScript 在单个线程上从“事件循环”一次执行一个代码块的行为。下面是从 Kyle Simpson 的书 ydkJS 中获取的事件循环的基本实现,然后是解释:
// `eventLoop` is an array that acts as a queue (first-in, first-out)
var eventLoop = [ ];
var event;
// keep going "forever"
while (true) {
// perform a "tick"
if (eventLoop.length > 0) {
// get the next event in the queue
event = eventLoop.shift();
// now, execute the next event
try {
event();
}
catch (err) {
reportError(err);
}
}
}
第一个 while 循环模拟事件循环。滴答是事件从“事件循环队列”中出列并执行所述事件。
请参阅 'Josh3796' 的回复,以更详细地说明在事件的出队和执行中发生的情况。
另外,我建议有兴趣深入了解 JavaScript 的人阅读 Kyle Simpson 的书。它是完全免费和开源的,可以在此链接中找到: https ://github.com/getify/You-Dont-Know-JS
我引用的具体部分可以在这里找到:https ://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/sync-async/ch1.md
事件循环刻度非常简单和简短的方法是:
它由节点内部机制使用,当处理队列上的一组请求时,会启动表示任务完成的滴答声
“滴答声”是指完整地通过事件循环。令人困惑的是, setImmediate() 需要一个滴答声才能运行,而 process.nextTick() 更直接,所以这两个函数应该换个名字。