awaitDrain++执行两次是作者不希望看到的情况,因为下游触发drain事件时
awaitDrain相应减1,直到其值为0时才让上游重新流动,如果
awaitDrain++执行两次,下游却只触发一次drain事件,
awaitDrain就不会为0,上游不重新流动也就无法继续读取数据。真相的探索过程
虽然从理性上认为
increasedAwaitDrain没起到作用,但也无法肯定加绝对,自己尝试去求助,没有出现高手指点出问题所在,但一个同事听我描述后,说可能这就是个BUG,虽心中觉得可能性不大,但还是抱着试试看的心态切换到master分支上去瞅瞅,随即发现最新的代码里并没有与
increasedAwaitDrain类似的逻辑,间接说明v8.11.1分支上
increasedAwaitDrain相关逻辑的确无用。虽然比较肯定这里存在一段无用代码,但应该如何理解作者在
increasedAwaitDrain上方的注释呢?为了进一步揭露真相,自己继续花时间去看了看stream.Readable相关代码,想知道data事件的触发时机与顺序是如何决定的。readable流的简单原理
在进一步解释data事件的触发顺序前,简单讲一下readable流的实现原理,如果需要自己实现一个readable流,可以使用new stream.Readable(options)方法,其中options可包含四个属性:highWaterMark、encoding、objectMode、read。最主要的是read属性,当流的使用者需要数据时,read方法被用来从数据源获取数据,然后通过
this.push(chunk)将数据传递给使用者,如果没有更多数据可供读取时使用
this.push(null)表示读取结束。
const Readable = require('stream').Readable;
let letter = 'ABCDEFG'.split('');
let index = 0;
const rs = new Readable({
read(size) {
this.push(letter[index++] || null);
}
});
rs.on('data', chunk => {
console.log(chunk.toString());
});
// 输出
// A
// B
// C
// ...这里ondata虽然没有明显调用read方法,但内部依旧是通过调用read方法结合this.push输出数据,并且在源代码内部可以发现通过参数传递的read方法实际上被赋值给this._read,然后在Readable.prototype.read中调用this._read获取数据。
灵魂代码
为了进一步说明stream.Readable的data事件触发顺序与场景,将有关官方源码经过修改和删减成如下:









