上次寫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分支上去瞅瞅,隨即發現最新的代碼里并沒有與
新聞熱點
疑難解答
圖片精選