You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何stream.read()或stream.once('readable')无错误终止Node.js进程?

Node.js 空文件读取中readable.read()与readable.once('readable')的行为解析

问题现象

读取空文件test.txt时,代码预期会抛出错误或无限挂起,但实际仅输出0、1后,进程以状态码0正常终止。

测试代码

const { createReadStream } = require('fs');

process.on('unhandledRejection', error => {
  console.log(error)
});

process.on('error', e => {
  console.log('error', e)
});

process.on('exit', e => {
  console.log('exit', e)
});

(async () => {
  // test.txt 是一个空文件
  const stream = createReadStream('test.txt', { encoding: 'utf-8' })
  const waitForReadable = () => new Promise(r => stream.once('readable', r))
  for (let i = 0; i < 1000; i += 1) {
    console.log(i)
    stream.read()
    await waitForReadable()
  }
})()

实际输出

0
1
exit 0

原因解析

这是Node.js Readable流特性与事件循环机制共同作用的结果,具体流程如下:

  1. 第一次循环(i=0)

    • 打印0后调用stream.read(),此时流仍在异步读取文件,返回null并继续执行读取流程。
    • 执行await waitForReadable()为流添加readable事件监听,随后流读取到空文件末尾,触发readable事件(流结束时会触发一次readable),Promise resolve,await完成后进入第二次循环。
  2. 第二次循环(i=1)

    • 打印1后调用stream.read(),此时流已读取到文件末尾,返回null,同时流触发end事件并关闭。
    • 执行await waitForReadable()再次添加readable监听,但流已结束,不会再触发readable事件,此时Promise处于pending状态。
  3. 进程退出

    • Node.js的事件循环会在没有待处理任务(如定时器、活跃流、网络请求等)时直接终止进程。由于流已关闭,没有其他事件源维持事件循环,即使存在pending的Promise,进程也会直接退出,导致循环无法继续执行后续迭代。

补充说明

  • Readable流在到达文件末尾时,会触发一次readable事件,此时调用read()会返回null表示流结束。
  • 流结束后,readable事件不会再次触发,因此后续添加的readable监听永远不会执行。
  • Node.js不会为pending的Promise单独维持事件循环,必须存在其他活跃的事件源(如未关闭的流、定时器等),进程才会持续运行。

内容的提问来源于stack exchange,提问作者Konrad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 17:05:58