为何绑定readable事件后process.stdin.read被调用两次?如何正确Mock?
为什么process.stdin.read会被调用两次?
这事儿我太熟了,本质是Node.js的Readable流内部机制在起作用,咱们一步步拆解:
两次调用的来源
你看到的两次read调用,分别来自两个地方:
- 第一次调用(无参数):就是你在
readable事件回调里显式写的process.stdin.read(),完全是你主动触发的。 - 第二次调用(参数为0):这是Node.js Readable流的内部自动调用。当
readable事件的用户回调执行完成后,流会自动调用read(0)——这个调用的目的是触发流的状态检查:确认是否还有剩余数据需要推送,或者是否要切换到flowing模式,再或者判断是否应该结束流。不管是自然触发的readable事件,还是你手动emit('readable'),这个内部调用都会发生。
你的Mock代码为什么出问题?
看你的callerFn逻辑,它把参数当成bool判断,当参数是0时返回null,否则就消耗生成器的yield值。但问题在于:
- 第一次调用是无参数(
args: []),此时bool是undefined,不等于0,所以会消耗生成器的第一个值; - 第二次内部调用传了0,虽然返回null,但如果后续还有业务逻辑需要读取数据,生成器已经没有值可以yield了——这就导致你的Mock无法正常提供后续数据。
修复方案
调整Mock逻辑,忽略size为0的内部调用,不消耗生成器的内容,同时规范处理read方法的参数(size表示读取的字节数,不是布尔值):
const mockStdinRead = (message) => { const gen = function * () { yield message } const it = gen() const callerFn = (size) => { // 对于内部的read(0)调用,直接返回null,不消耗生成器 if (size === 0) return null; // 读取生成器的值,没有值时返回null(符合流的结束规范) const result = it.next().value; return result ?? null; } return callerFn }
用这个Mock测试你的代码,就能看到:
- 你主动调用的
read()会拿到预设的message; - 内部的
read(0)调用只会返回null,不会消耗生成器的内容,完全不影响你的业务逻辑。
内容的提问来源于stack exchange,提问作者ThomasReggi
相关产品推荐
相关产品推荐

