为何将JavaScript readline的question方法包装进Promise会读取多行?
Node.js readline.question 文件重定向时的行为差异原因
问题场景
使用readline模块编写的代码,手动在命令行输入时能正常逐行读取并添加行号,但通过文件重定向(如node testReadline.mjs < the_file.txt)输入时,会一次性读取整个文件内容,输出不符合预期。
预期输出
Next line, please: File line 1 1 File line 1 Next line, please: File line 2 2 File line 2 Next line, please: File line 3 3 File line 3 Next line, please: File line 4 4 File line 4 Next line, please: Input stream closed.
实际输出
Next line, please: File line 1 File line 2 File line 3 File line 4 1 File line 1 Next line, please: Input stream closed.
异步await实现代码(不符合预期)
import * as readline from 'node:readline'; const io = readline.createInterface({ input: process.stdin, output: process.stdout, }); io.on('close', () => { console.log("Input stream closed.") }); let questionWrapper = (prompt) => { return new Promise((resolve, reject) => { io.question(prompt, (line) => { resolve(line) }); }); } let printLine_await = async () => { let line_num = 1; while (true) { let line = await questionWrapper('Next line, please: '); console.log(`${line_num}\t${line}`) line_num++; } } printLine_await(1)
回调实现代码(符合预期)
let printLine_callback = (line_num) => { io.question('Next line, please: ', (line) => { console.log(`${line_num}\t${line}`) printLine_callback(line_num + 1) }) }
原因解析
异步await版本异常的核心原因
- 文件输入的缓冲特性:文件重定向输入时,Node.js会一次性把整个文件内容加载到标准输入缓冲区,而非手动输入时的逐行触发。
question的事件监听逻辑:io.question()执行时,会先输出提示,然后监听输入流的line事件。但缓冲区已有多行内容时,第一个question只会捕获第一行,剩余行仍在缓冲区中。- 异步时序的间隙问题:
await会暂停循环,等待第一个Promise完成。这期间,输入流中剩余的内容会直接被输出到终端(因为readline没有暂停输入流),不会被后续的question调用捕获。当缓冲区内容全部输出后,输入流关闭,循环直接终止。
回调版本正常的原因
回调实现中,每处理完一行后才会发起下一次question调用。每次question都会重新绑定line事件监听,此时缓冲区中的下一行会被当前question正确捕获,不会直接输出到终端,从而实现逐行处理的效果。
内容的提问来源于stack exchange,提问作者Zack
相关产品推荐
相关产品推荐

