Object模式下stream.write回调未全部触发的原因及模式差异解析
Object模式下stream.write回调未全部触发的原因及模式差异解析
嘿,这个问题其实戳中了Node.js流在object模式和默认字节模式下的核心行为差异,咱们一步步来拆解清楚~
先搞懂为什么objectMode下只触发15次回调
你遇到的情况和Node.js流的缓冲区限制直接相关:
- 当开启
objectMode: true时,PassThrough流的内部缓冲区是按对象数量来限制的,默认的highWaterMark值是16个对象。 - 你的循环里写了20个对象,当写到第16个时,缓冲区就满了,这时候
ps.write()会返回false,表示当前无法立即写入,后续的写入操作(第16到19个)会被放到队列里等待。 - 但你的程序写完20次循环后直接退出了,既没有调用
ps.end()来通知流处理剩余缓存,也没有等待drain事件(缓冲区清空时触发),所以队列里的那5个写入操作根本没机会执行,它们的回调自然也不会被触发。
你看到的15次日志,其实是前15个对象成功写入缓冲区并触发回调,第16个写入时缓冲区满了,回调还没来得及执行程序就结束了。
为什么非object模式下所有回调都能触发?
当关闭objectMode时,流切换到字节模式:
- 这时缓冲区的限制是按字节数计算的,默认
highWaterMark是16KB(16384字节)。 - 你写入的每个
JSON.stringify({n: i})字符串非常小,20个加起来的总字节数远小于16KB,所以所有写入操作都能立即完成,缓冲区从来没满过,自然所有回调都能在程序退出前被触发。
验证和解决方法
如果想让object模式下的所有回调都触发,只需要在循环结束后通知流处理剩余数据,并等待流完成:
const stream = require('stream'); const ps = new stream.PassThrough({objectMode: true}); for (let i = 0; i < 20; i++) { const writeable = ps.write(JSON.stringify({n: i}), () => { console.log('write', i); }); } // 通知流没有更多数据要写入 ps.end(); // 等待流处理完所有缓存数据并结束 ps.on('finish', () => { console.log('所有写入操作已完成'); }); ps.on('error', (err) => { console.log('error', err); });
运行这段代码,你会看到20次write日志,最后输出所有写入操作已完成。
两种模式的核心差异总结
- 缓冲区限制维度不同:
- object模式:按对象数量限制(默认16个)
- 字节模式:按字节数限制(默认16KB)
- 写入排队逻辑:当缓冲区达到上限时,
write()返回false,后续写入会排队;如果程序不等待流处理完缓存就退出,排队的操作会被丢弃 - 触发时机差异:字节模式下小数据量很难填满缓冲区,而object模式下少量对象就可能触发缓冲区上限
备注:内容来源于stack exchange,提问作者Sello Mkantjwa
相关产品推荐
相关产品推荐

