上次写Stream pipe细节时,在源码中发现一段无用逻辑,由此引发了对Stream data事件触发时机与顺序的探索。
无用逻辑
当时研究pipe细节是基于Node.js v8.11.1的源码,其中针对上游的ondata事件处理有如下一段代码:
// If the user pushes more data while we're writing to dest then we'll end up// in ondata again. However, we only want to increase awaitDrain once because// dest will only emit one 'drain' event for the multiple writes.// => Introduce a guard on increasing awaitDrain.var increasedAwaitDrain = false;src.on('data', ondata);function ondata(chunk) { debug('ondata'); increasedAwaitDrain = false; var ret = dest.write(chunk); if (false === ret && !increasedAwaitDrain) { if (((state.pipesCount === 1 && state.pipes === dest) || (state.pipesCount > 1 && state.pipes.indexOf(dest) !== -1)) && !cleanedUp) { debug('false write response, pause', src._readableState.awaitDrain); src._readableState.awaitDrain++; increasedAwaitDrain = true; } src.pause(); }}
重点关注increasedAwaitDrain
变量,理解这个变量期望达到什么目的,然后仔细阅读代码,会发现if (false === ret && !increasedAwaitDrain)
语句中increasedAwaitDrain
变量肯定是false,因为前一行才将该变量赋值为false,这样一来这个变量就变得毫无意义。
increasedAwaitDrain = false; var ret = dest.write(chunk); if (false === ret && !increasedAwaitDrain) {}
以上就是关键的三行代码,因为Node.js是单线程且dest.write(chunk)
内部没有修改变量increasedAwaitDrain
的值,那么if语句中increasedAwaitDrain
的值肯定还是false,即increasedAwaitDrain
相关逻辑没有达到所期望的目标。
无用代码出现的原因
前段虽已经分析出increasedAwaitDrain
没起到作用,但作者为什么写了这样一段逻辑呢?其实在定义increasedAwaitDrain
语句的上方,作者说可能存在这样一种情况:“当我们接收到一次上游的ondata事件并尝试将数据写到下游时,上游可能同时又有一个data事件触发,而这两个ondata的数据在写入下游时可能都返回false,从而导致src._readableState.awaitDrain++
执行两次”。
awaitDrain++
执行两次是作者不希望看到的情况,因为下游触发drain事件时awaitDrain
相应减1,直到其值为0时才让上游重新流动,如果awaitDrain++
执行两次,下游却只触发一次drain事件,awaitDrain
就不会为0,上游不重新流动也就无法继续读取数据。
真相的探索过程
虽然从理性上认为increasedAwaitDrain
没起到作用,但也无法肯定加绝对,自己尝试去求助,没有出现高手指点出问题所在,但一个同事听我描述后,说可能这就是个BUG,虽心中觉得可能性不大,但还是抱着试试看的心态切换到master分支上去瞅瞅,随即发现最新的代码里并没有与
新闻热点
疑难解答
图片精选