为什么等待导致我的流被读取

Why does an await cause my stream to be read

我有一个从文件创建的流,我打算稍后使用它。但是,我注意到如果我等待 运行 的任何异步代码,即使它与我的流完全无关,它也会导致流被读取。为什么会这样?

代码在服务器端。

const data = fs.createReadStream(filePath);
console.log(data.bytesRead); // output: 0
await new Promise(r => setTimeout(r, 2000));
console.log(data.bytesRead); // output: 65536

此解释适用于节点 v14 和之前的版本...节点 v15+ 中的行为已更改,当我在 v16 中测试时,两个 data.bytesRead 值都显示 0

fs.createReadStream() 为文件调用异步 fs.open() 之后,它会读取文件的第一个块,以便在流开始流动时可用于流式传输。这是内部预缓冲。这可能是因为它提高了性能。如果您假设 reader 将读取整个流(或其中的大部分),那么您也可以在文件打开成功后立即获取内容的第一个缓冲区,即使流不是仍然流动,或者即使调用者没有被手动调用来读取一些字节。

因为读取是异步的,结果直到您的 await 造成的延迟之后才会显示(读取在您的 await 期间完成)。所以,这就是 await 允许您看到值变化的原因。

如果您想使用 fs.createReadStream() 跟踪代码,您可以在调试器中单步执行它并准确观察它的作用。 ReadStream 构造函数中的代码结构在 v14 和最新版本之间发生了很大变化,因此如果您将在 Github 源代码中看到的内容与在调试器中逐步执行它进行比较,您可能会感到困惑如果两者不是同一个版本(这发生在我身上)。尽管在最近的版本中实现发生了变化,但想法是一样的(预取第一块内容)。

首先,它调用 ReadStream 构造函数。然后,作为其中的一部分,它打开文件并在文件打开成功后,将文件的第一个块读入其当前缓冲区。


看来这个 Github issue 似乎负责更改 readStream,使其不再自动开始读取,相应的代码更改看起来像是进入了 v15。

在 v16 ReadStream 代码 here 中,您现在可以看到文件打开的位置(在新的 _construct 方法中)并且在打开文件后,不再有任何代码可以预先-获取文件的第一个块。