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

如何将Node.js的fs.createReadStream封装为Promise,解决end事件提前触发问题

问题根因

你遇到的end回调提前触发问题,核心是Node.js流的事件系统不会等待异步回调执行完成:你给data事件绑定的async函数返回的Promise会被事件系统直接忽略,流只负责按顺序派发事件,不会等待回调内部的异步逻辑执行完。即便你调用了stream.pause(),最后一次data事件派发时流已经读完了所有数据,此时end事件会同步触发,不会等你当前正在执行的await row(data)逻辑跑完。


1. 如何让end回调等待所有处理逻辑完成

你可以新增两个状态标记,分别记录当前是否有正在执行的异步row任务、以及流是否已经触发end事件,等最后一个row任务执行完成后再触发resolve即可,修改后代码如下:

import * as fs from 'fs';

function parseFile<T>(filePath: string, row: (x: T) => Promise<void>) {
  return new Promise((resolve, reject) => {
    const stream = fs.createReadStream(filePath);
    // 记录当前是否有正在处理的row任务
    let processing = false;
    // 标记流是否已经触发end事件
    let endReceived = false;

    stream.on('data', async (data) => {
      processing = true;
      try {
        stream.pause();
        await row(data as T);
      } catch (e) {
        reject(e);
      } finally {
        processing = false;
        stream.resume();
        // 处理完当前任务后如果已经收到end事件,直接resolve
        if (endReceived) resolve();
      }
    })
    .on('end', () => {
      endReceived = true;
      // 如果当前没有正在处理的任务直接resolve,否则等最后一个任务处理完再resolve
      if (!processing) resolve();
    })
    .on('error', (err) => {
      reject(err);
    });
  });
}

注意传入的row函数必须是返回Promise的异步函数,否则await不会生效。修改后你原有的测试用例就会输出预期的1000而非1。


2. close事件未触发的原因

fs.createReadStream的close事件是在文件描述符被完全关闭后才会触发,你当前代码未触发该事件,通常是因为流触发end后还有未执行完的异步逻辑持有相关资源引用。就算close事件能正常触发,它和end事件一样是流层面的完成通知,依然不会等待你自定义的异步row任务执行完成,所以无法直接用它解决提前resolve的问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 17:15:03