99

我一直在深入了解 Node.js 架构的内部结构,我经常看到的一个术语是“tick”,如“事件循环的下一个滴答声”或函数nextTick()

我还没有看到对“tick”到底是什么的明确定义。根据各种文章(例如这篇文章),我已经能够在脑海中拼凑出一个概念,但我不确定它有多准确。

我可以得到一个 Node.js 事件循环刻度的准确和详细的描述吗?

4

4 回答 4

172

请记住,虽然 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
  • I/O(来自fsnet等的东西)
  • crypto的处理器密集型功能,例如加密流、pbkdf2 和 PRNG(实际上是...的一个示例)
  • 任何使用libuv 工作队列使同步 C/C++ 库调用看起来是异步的本机模块
于 2013-11-06T21:52:02.267 回答
13

对于 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

于 2018-02-18T01:33:26.470 回答
2

事件循环刻度非常简单和简短的方法是:

它由节点内部机制使用,当处理队列上的一组请求时,会启动表示任务完成的滴答声

于 2019-09-06T05:47:36.400 回答
1

“滴答声”是指完整地通过事件循环。令人困惑的是, setImmediate() 需要一个滴答声才能运行,而 process.nextTick() 更直接,所以这两个函数应该换个名字。

于 2021-02-04T06:26:55.523 回答