在看koa-body的時候,發現很多和流相關的操作,就去看了下node文檔和其他文章, 花了挺長時間看完以後發現還只是略知一二,分享記錄一下。很多細節還是要看官方文檔。
stream是個啥
個人理解,就是個可以控制讀寫速度的緩衝區的封裝,方便大家處理數據,比如說我要讀取一個文件,我如果直接使用fs.readFile,不管同步異步吧,總是一口氣讀出來的,那麼同步操作會阻塞,異步操作的話至少文件比較大的時候佔的緩存空間是很多的。聰明的你會說,那還可以用fs.read呀,可以自己設置偏移量,自由度很高。是的,不過如果偷懶不想寫偏移相關的邏輯的話,你就可以直接使用fs.createReadStream來操作,它可以幫助你最小化內存成本。
讀取是這樣,寫入也是一樣的。
而node提供的stream模塊,其實就是來幫助我們自己開發一些類似fs.createReadStream這樣的自定義的功能。node也有很多封裝好的stream,fs.createReadStream顯然就是一例,然後http模塊的req和res也是,還有很多。而一般上來就直接拿stream模塊來使用的,不多,大多是繼承它。
純屬個人理解,可能不太準確,如果看了下面一堆東西感覺沒明白,請再看一遍上面這段話。
到底讀還是寫
stream文檔上來就跟你說,我們有四種,可讀流、可寫流、雙工流、轉換流
不管後面兩種不知道在說什麼的流,先看可讀流和可寫流是怎麼回事,可讀流到底是要讀還是要寫,先看一個例子。
const Readable = require("stream").Readable;
class ToReadable extends Readable {
constructor(num) {
super();
this.num = num;
}
// 生產數據的邏輯
_read() {
if (this.num <= 0) {
// push(null)表示沒有數據了
return this.push(null);
}
setTimeout(() => {
// 通過push方法將數據添加到流中
this.push("" + this.num--);
}, 500);
// 同步調用也一樣的
// this.push("" + this.num--);
}
}
const readable = new ToReadable(10);
// 監聽`data`事件,一次獲取一個數據
readable.on("data", data => console.log(data.toString()));
// 所有數據均已讀完
readable.on("end", () => console.log("DONE"));
運行一下看看,然後體會一下就會發現,可讀流,其實是要開發某個自定義模塊的人(一般就是你了)做push數據操作,讓使用自定義模塊的人可以從模塊讀數據,可寫流,顯然就是反過來,用模塊的人寫數據,開發模塊的人(你),讀數據,看一個可寫流的例子。
const Writable = require("stream").Writable;
const writable = Writable();
writable._write = function(data, enc, callback) {
console.log(data.toString().toUpperCase());
// 寫入完成時,調用next方法通知流傳入下一個數據
process.nextTick(callback);
// 同步調用也一樣的
// callback();
};
// 數據寫入完畢
writable.on("finish", () => console.log("DONE"));
// 將數據寫入流中
writable.write("a");
writable.write("b");
writable.write("c");
// 數據寫入結束,需要調用end方法
writable.end();
理解了可讀和可寫以後,雙工和轉換其實就容易了。
因爲細節很多而且自己一知半解所以不說太多,有興趣深入瞭解的,大家可以自己看文檔或者其他文章
參考文章:
http://nodejs.cn/api/stream.html#stream_readable_read_size
https://tech.meituan.com/2016/07/08/stream-basics.html
https://tech.meituan.com/2016/07/15/stream-internals.html