关于 NodeJS 的 Stream 模块中的 readable 事件问题
发布于 9 年前 作者 lipengyu2006 4527 次浏览 最后一次编辑是 8 年前 来自 问答

先帖段简单的代码(t1.js)

process.stdin.on(‘readable’, function() { var buf = process.stdin.read(3); console.log(buf); process.stdin.read(0); });

然后在终端中运行命令①

(echo abc; sleep 1; echo def; sleep 1; echo ghi) | node t1.js

结果如下:

<Buffer 61 62 63> <Buffer 0a 64 65> <Buffer 66 0a 67> <Buffer 68 69 0a>

然后在终端中运行命令②

(echo abc; echo def; echo ghi) | node t1.js

结果如下:

<Buffer 61 62 63> <Buffer 0a 64 65>

我的问题是: 照成不同结果的原因是什么呢? 为什么不加 sleep 会出现丢失数据的情况? readable 事件为什么会退出?(readable的机制) 命令行管道符问题?

希望哪位高人能够解答

3 回复

你可以用’data’事件处理下: process.stdin.on('data', function(data) { console.log('data:' + data); }); 不过这样read就会返回空

探索了一下楼主的问题

先看管道的问题:
$(echo abc; sleep 1; echo def; sleep 1; echo ghi) | xargs echo
$(echo abc;  echo def;  echo ghi) | xargs echo

输出结果一样,区别在于第一个要等三秒第二个直接打印了,所以传给node的buffer应该是一样的。

做了两个测试,readable被调用了几次(全局变量步增),是否跟var buf = process.stdin.read(3);里面的3有关系,代码就不贴了; 两个测试结果: 楼主第一种情况handler被调用了4次,第二次被调用了2次。把3变为10后,第二种情况得到了一个长度为10的buffer+长度为2的buffer; 两个测试结论: 输出几次跟着两个有点关系,触发几次handler就会有几次输出,而每次read多少长度影响了接收到的buffer里的值是否能被全部读取(未读取的buffer被放在缓冲区)。

进一步,onsole.log(process.stdin);,发现process.stdin._readableState.buffer有点像缓冲区… 把代码改为:

var buffers = [];
var i = 0;
process.stdin.on('readable', function() {
  buffers.push(process.stdin.read(3));
  console.log(i++, buffers, process.stdin._readableState.buffer);
  process.stdin.read(0);
});

结果1:

//(echo abc; sleep 1; echo def; sleep 1; echo ghi) | node t1.js
0 [ <Buffer 61 62 63> ] [ <Buffer 0a> ]
1 [ <Buffer 61 62 63>, <Buffer 0a 64 65> ] [ <Buffer 66 0a> ]
2 [ <Buffer 61 62 63>, <Buffer 0a 64 65>, <Buffer 66 0a 67> ] [ <Buffer 68 69 0a> ]
3 [ <Buffer 61 62 63>,
  <Buffer 0a 64 65>,
  <Buffer 66 0a 67>,
  <Buffer 68 69 0a> ] []

结果2:

//(echo abc;  echo def;  echo ghi) | node t1.js
0 [ <Buffer 61 62 63> ] [ <Buffer 0a 64 65 66 0a 67 68 69 0a> ]
1 [ <Buffer 61 62 63>, <Buffer 0a 64 65> ] [ <Buffer 66 0a 67 68 69 0a> ]
结论

两次结果不一样是readable触发次数+read(N)中N的数量导致的。 而从结果来看 (echo abc; echo def; echo ghi) 只触发了一次readable,而断开前又会触发一次readable。 (echo abc; sleep 1; echo def; sleep 1; echo ghi) 则触发了 3+1 是 4 次。

readable 事件为什么会退出,是一个ctl+d…可以用 node t1.js 然后输入测试数据,最后 ctrl+d 退出,跟echo的效果一致

➜  ~  node t1
0 [ null ] []
abc
1 [ null, <Buffer 61 62 63> ] [ <Buffer 0a> ]
def
2 [ null, <Buffer 61 62 63>, <Buffer 0a 64 65> ] [ <Buffer 66 0a> ]
ghi
3 [ null, <Buffer 61 62 63>, <Buffer 0a 64 65>, <Buffer 66 0a 67> ] [ <Buffer 68 69 0a> ]
4 [ null,
  <Buffer 61 62 63>,
  <Buffer 0a 64 65>,
  <Buffer 66 0a 67>,
  <Buffer 68 69 0a> ] []
end
close
➜  ~

@albin3 非常感激您这么认真测试这个小程序。 我的理解如下: readable 事件由底层系统决定触发次数。 我们只能处理每次事件 drain 过来的数据。 这些数据可以一次全都 read 或 read(N)。 当 read(N) 时,N 小于本次 drain 过来数据的长度时,需要继续read(N),直到结束。 显然 read(0) 这个方法有时候并不可靠。

再您基础上我又做了如下修改:

var buffers = []; var i = 0; process.stdin.on(‘readable’, function() { var chunk; i++; while (null !== (chunk = process.stdin.read(3))) { buffers.push(chunk); console.log(i, buffers, process.stdin._readableState.buffer); } });

这次得到了我想要的效果,再次感谢您 @albin3

回到顶部