node.js - 在 readline 启动后定义事件处理程序
问题描述
在 NodeJS 中逐行读取文件的教科书方法似乎是调用readline.createInterface
,然后为line
和附加事件处理程序close
。
似乎没有任何东西可以“启动”读者。它只是去,似乎完美地工作。它如何知道何时开始阅读?它如何保证那些尚不存在的事件总是会占用文件中的每一行?
我一直认为这一切都发生得如此之快,以至于事件的附加速度比从磁盘打开文件并开始读取它的速度要快——但这并没有真正成立。
例如,假设我在创建 lineReader 之后,但在附加事件之前放置了一些占用大量 CPU 的代码。它似乎仍然有效,并且事件仍然为每一行触发。它是如何“等到”繁重的工作完成后才开始阅读的?如果我不附加line
事件,那么它无论如何都会运行并且close
事件仍然会触发,所以它不像是在等待line
事件被创建。
var lineReader = readline.createInterface({
input: fs.createReadStream("input.txt")
});
// EVENTS HAVE NOT BEEN CREATED YET
lineReader.on("line", line => { console.log(line); });
lineReader.on("close", () => { console.log("DONE"); });
这不是特定于lineReader
- 似乎是一种常见的节点模式 - 这只是最容易定义和运行的。
解决方案
在内部,readline.createInterface()
正在创建一个流。默认情况下,流是暂停的。它们以多种方式取消暂停,这里相关的data
是添加事件侦听器时。
并且,在 内部readline.createInterface()
,添加了一个data
事件处理程序。这开始流流动,它将开始发出data
readline 代码将解析为 line 事件的事件。
此外,由于 node.js 和流是事件驱动的,并且 node.js 将您的 Javascript 作为单线程运行,这意味着在您的设置代码完成执行之前不会发生任何事件。在内部,node.js 可能已经开始读取文件(在内部使用异步 I/O 和线程),但即使它在您的设置代码完成执行之前完成了对文件的第一次读取,它所做的只是在其中插入一个data
事件事件队列。data
在您的设置代码执行完毕并将控制权返回给 node.js 事件循环之前,node.js 不会处理该事件。
然后,data
将调用事件回调,readline 代码将解析来自第一个事件的数据,如果第一个数据事件中有一个完整的行,它将触发一个line
事件。
似乎没有任何东西可以“启动”读者。
在 readStream 上附加一个data
事件处理程序(在 readline 代码内部)是告诉流开始流动的原因。
它只是去,似乎完美地工作。它如何知道何时开始阅读?
和上面一样。
它如何保证那些尚不存在的事件总是会占用文件中的每一行?
readline 代码在其data
事件处理程序中从文件接收原始数据。然后它将该代码解析为行并line
为它找到的每一行发出事件。当文件读取跨越行边界时,它必须缓冲部分行并等待该行的其余部分出现在data
流中的下一个事件上。
当 linereader 代码看到流已完成读取并且没有更多字节时,它会发送最后一行(如果缓冲区中有一行),然后发出close
事件告诉侦听器它已完成。
例如,假设我在创建 lineReader 之后,但在附加事件之前放置了一些占用大量 CPU 的代码。它似乎仍然有效,并且事件仍然为每一行触发。它是如何“等到”繁重的工作完成后才开始阅读的?
这是因为 node.js 是事件驱动的。流中的第一个data
事件(readline 代码内部)是fs.readFile()
函数的结果,它通过事件队列通知完成。直到当前的 Javascript 完成并将控制权返回给事件循环(此时它将为事件队列中等待的下一个事件提供服务),才会处理事件队列中的事件。因此,无论在附加事件处理程序之前有多少消耗 CPU 的代码,readline 的内部都不会被告知从文件中读取的第一个数据,直到所有这些都完成。
正是这种单线程、事件驱动的特性确保您可以在触发这些事件之前安装事件侦听器,这样您就不会错过它们。
如果我不附加 line 事件,那么它无论如何都会运行并且 close 事件仍然会触发,所以它不像是在等待 line 事件被创建。
正确的。无论您是否有事件侦听器, readline 代码都会data
在调用中附加事件处理程序。因此,流将开始流动,并且无论您是否有事件处理程序,文件都会被读取。createInterface()
line
line
仅供参考,您可以自己帮助回答这些问题的一种方法是查看 node.js 代码,看看它是如何工作的。这就是我在这里所做的。这是该功能的链接createInterface()
,您可以在其中看到我在此处描述的内容。
而且,您可以在流文档中看到这里,其中描述了流开始流动的三种方式,其中一种是附加data
事件侦听器。
推荐阅读
- active-directory - PHP LDAP 在连接到 GC 3268 时未获取成员对象
- function - 为什么创建 exe 后 PowerShell 配置文件不加载功能
- javascript - Phaser 3 对撞机无限开火
- python - Sagemaker 回归 - ValueError:无法格式化输入
- java - Need to eliminate the duplicate schema from two different list
- angular - 如何根据角度10中的条件设置选择
- javascript - 如果单击/选择子选项,则在鼠标移出时保持子菜单打开
- android - AVD Pixel_3a_API_28 的模拟器进程被杀死
- javascript - 弹出选项卡的第二个选项中的内容不显示任何内容
- python - 为什么我的冒泡排序不能正常工作?