有没有其他方法可以在没有无限循环的情况下实现 "listening" 函数?

Is there any other way to implement a "listening" function without an infinite while loop?

我一直在思考像 React 这样的代码和库,它们会在事件发生时自动对事件做出反应,并且想知道所有这些是如何在 C++ 和机器代码的较低级别上实现的。

我似乎无法想出任何其他方法来实现事件监听器之类的东西,if not with while loop 运行 on another thread。

所以这一切都在幕后吗?只是 while 一直循环下去?例如,RethinkDB 将自己宣传为具有 repubsub 库的 "realtime database"。 "subscribe" 方法只是在幕后使用 while 循环实现的吗?我似乎找不到任何相关信息。

还有套接字之类的东西。当一台计算机在套接字连接的端口上是 "listening" 时,那台计算机是否只是 运行 类似于:

while(1) {
    if (connectionFound) {
        return True;
    }
}

或者我遗漏了什么?

"listeners" 和 "subscriptions" 只是想法。一切都可以用 lambdas 抽象。这是一种可能的实现 -

const logger =
  // create a new "listener",
  // send any data we "hear" to console.log
  listen(console.log)

// implement so-called "listener"
const listen = (responder) =>
  x => (responder(x), x)

// run it synchronously
logger(1)
logger(2)

// or asynchronously
setTimeout(_ => logger(3), 2000)

// 1
// 2
// some time later...
// 3

假设我们有一个

I've written the answer to this question as an aside in another answer. Normally I'd close this question as a duplicate and point to that answer however this is a very different question. The other question asked about javascript performance. In order to answer that I had to first write the answer to this question.

As such I'm going to do something that's not normally supposed to be done: I'm going to copy part of my answer to another question. So here's my answer:

javascript 和 node.js 等待的实际事件根本不需要循环。事实上,他们需要 0% CPU 时间。

异步 I/O 的工作原理(在任何编程语言中)

硬件

如果我们真的需要了解节点(或浏览器)内部的工作原理,我们必须首先了解计算机的工作原理 - 从硬件到操作系统。是的,这将是一次深入的探索,所以请耐心等待..

一切都始于中断的发明..

It was a great invention, but also a Box of Pandora - Edsger Dijkstra

是的,上面的引述来自同一个“Goto considered harmful”Dijkstra。从一开始就将异步操作引入计算机硬件被认为是一个非常困难的话题,即使对于业内的一些传奇人物也是如此。

引入中断以加速 I/O 操作。无需在无限循环中使用软件轮询某些输入(从有用的工作中占用 CPU 时间),硬件将向 CPU 发送信号以告知其事件已发生。然后 CPU 将挂起当前 运行ning 程序并执行另一个程序来处理中断 - 因此我们称这些函数为中断处理程序。 “处理程序” 这个词一直沿用到 GUI 调用回调函数“事件处理程序”的库。

维基百科实际上有一篇关于中断的相当不错的文章,如果您不熟悉中断并想了解更多:https://en.wikipedia.org/wiki/Interrupt.

如果您一直在注意,您会注意到中断处理程序的概念实际上是一个回调。您将 CPU 配置为在事件发生后的某个时间调用函数。所以即使是回调也不是一个新概念——它比 C 更古老。

OS

中断使现代操作系统成为可能。如果没有中断,CPU 将无法暂时停止您的程序到 运行 OS(好吧,有协作式多任务处理,但让我们暂时忽略它)。 OS 的工作原理是它在 CPU 中设置一个硬件计时器来触发中断,然后它告诉 CPU 执行您的程序。 运行 就是您的 OS.

的周期性定时器中断

除了定时器之外,OS(或者更确切地说是设备驱动程序)为 I/O 设置了中断。当 I/O 事件发生时,OS 将接管您的 CPU(或多核系统中的 CPU 之一)并检查其处理它的数据结构需要执行 next 来处理 I/O(这称为抢占式多任务处理)。

从键盘和鼠标到存储器再到网卡,一切都使用中断来告诉系统有数据要读取。如果没有这些中断,监控所有这些输入将占用大量 CPU 资源。中断非常重要,它们通常被设计成 I/O 标准,例如 USB 和 PCI。

进程

现在我们已经清楚地了解了这一点,我们可以理解 node/javascript 实际上是如何处理 I/O 和事件的。

对于 I/O,各种 OSes 具有各种不同的 APIs,它们提供异步 I/O - 来自重叠 I/O on Windows到 Linux 上的 poll/epoll 到 BSD 上的 kqueue 到跨平台 select()。 Node 在内部使用 libuv 作为对这些 API 的高级抽象。

虽然细节不同,但这些 API 的工作原理是相似的。本质上,它们提供了一个函数,该函数在被调用时会阻塞您的线程,直到 OS 向其发送事件。所以是的,即使是非阻塞 I/O 也会阻塞你的线程。这里的关键是阻塞 I/O 会在多个地方阻塞你的线程,但非阻塞 I/O 只会在一个地方阻塞你的线程——你等待事件的地方。

查看我对另一个问题的回答,以获取有关这种 API 如何在 C/C++ 级别工作的更具体示例:

对于 GUI 事件,如按钮单击和鼠标移动,OS 只需跟踪您的鼠标和键盘中断,然后将它们转换为 UI 事件。这使您的软件无需了解按钮、windows、图标等的位置

这允许您以面向事件的方式设计您的程序。这类似于中断允许 OS 设计人员实现多任务处理的方式。实际上,异步 I/O 之于框架就像中断之于 OSes。它允许 javascript 花费正好 0% CPU 的时间来处理(等待)I/O。这就是使异步代码快速的原因 - 它不是真的更快但不会浪费时间等待。

这个答案相当长,所以我将留下我对与此主题相关的其他问题的答案的链接:

(注意:这个答案提供了一些关于事件和线程关系的见解 - 简而言之:OS 在内核事件之上实现线程)

Does javascript process using an elastic racetrack algorithm

how node.js server is better than thread based server