只要有玩過 nodejs,那就一定接觸過 Writable。 http 模組的請求回呼參數中的 res 參數就是一個 Writable 物件。我們常常會往上面 write 一堆東西,最後呼叫個 end 方法吧?這些都屬於 Writable 的行為。
我們手動建立的 Writable 物件是交給使用者使用的,那麼 write 和 end 方法都是使用者呼叫的。作為提供方,我們如何知道自己的 Writable 物件被使用者執行了什麼操作?就猜這個 API 吧,我會先猜到某個事件。然而並不是!和 Readable 一樣,它也得覆寫某個方法來監聽操作。以下是建立一個 Writable 讓使用者往裡面寫入內容,並監聽使用者到底寫了什麼的例子(基於 babel-node):
import stream from 'stream'; var w = new stream.Writable; w._write = (buffer, enc, next) => { console.log(buffer + ''); next(); // 触发「写入完成」 }; w.on('finish', () => { console.log('finish'); }); void function callee(i) { if(i < 10) { w.write(i + '', 'utf-8', () => { // 写入完成 }); } else { w.end(); } setTimeout(callee, 10, i + 1); }(0);
同 Readable 的 _read 一樣,如果上面的 _write 沒有被覆寫將拋出異常:
Error: not implemented at Writable._write (_stream_writable.js:430:6) at doWrite (_stream_writable.js:301:12)
另外,write 被設計為一個非同步方法,它又第三個參數可以傳入完成的回呼。而所謂完成就是在實作函數 _write 中,next 參數被呼叫。把 write 設計成非同步是有原因的,如果它是同步執行,那麼當我們需要在 _write 方法中處理一些非同步事務時就可能產生順序出錯。例如一個磁碟檔案的寫入操作就是一個非同步的,如果我們寫檔案無視這個非同步,那麼假如上一個寫入操作被阻塞還沒完成,目前的寫入操作可能會先執行。所以我們應該在 _write 中合理地調用 next(必須調用,否則將陷入等待,無法繼續寫)。
最後,當資料寫完成後會觸發 finish 事件,這就表示 end 方法被使用者呼叫了。如果其間做的是寫文件的操作,此時就應該關閉文件。