前言
在編寫代碼時(shí)锰茉,我們應(yīng)該有一些方法將程序像連接水管一樣連接起來(lái) -- 當(dāng)我們需要獲取一些數(shù)據(jù)時(shí),可以去通過(guò)"擰"其他的部分來(lái)達(dá)到目的。這也應(yīng)該是IO應(yīng)有的方式珍昨。 -- Doug McIlroy. October 11, 1964
本質(zhì)上來(lái)說(shuō)双揪,編碼就是對(duì)數(shù)據(jù)的讀取动羽,處理最后返回結(jié)果,數(shù)據(jù)在一個(gè)程序又一個(gè)程序中不斷傳遞渔期。理想情況下运吓,數(shù)據(jù)的傳遞應(yīng)該是不停滯的,但是現(xiàn)實(shí)情況中因?yàn)橹T如單個(gè)數(shù)據(jù)過(guò)大疯趟,內(nèi)存較小拘哨,IO處理較慢等客觀原因使數(shù)據(jù)不能流暢的流動(dòng)
起來(lái)。這時(shí)我們就需要一種方法去將數(shù)據(jù)拆分成一小塊一小塊的數(shù)據(jù)(chunks),流水一樣的讀取處理寫入迅办。這種方法便是流(stream)
宅静,nodejs中的流主要分為以下四種:
- Readable(可讀流)
- Writable(可寫流)
- Duplex(可讀寫流)
- Transform (變換流)
而所有的流都有一種公有方法,它會(huì)像管道一樣處理流的數(shù)據(jù)站欺,這便是pipe
,下面一張大神制作的圖很好的演示了這個(gè)過(guò)程:
正文
pipe期望的使用方式
pipe
函數(shù)需要將源頭src
并將數(shù)據(jù)輸出到一個(gè)可寫的流dst
中:
src.pipe(dst)
pipe
將會(huì)返回dst
,所以我們希望可以鏈?zhǔn)秸{(diào)用將多個(gè)流用管道連接起來(lái):
a.pipe(b).pipe(c).pipe(d)
pipe的簡(jiǎn)化源碼分析
為了實(shí)現(xiàn)以上期望的使用方式姨夹,我們將Nodejs源碼簡(jiǎn)化一下看是怎樣實(shí)現(xiàn)的
stream.prototype.pipe = function(dest, options) {
this.on('data', (chunk) => {
if (dest.writable) {
if (false === dest.write(chunk) && this.pause) {
this.pause();
}
}
});
dest.on('drain', () => {
this.resume();
});
dest.emit('pipe', this);
return dest;
};
以上代碼主要做了4件事:
- 可讀流監(jiān)聽
data
事件
1)首先判斷dest.Writable
,當(dāng)寫完時(shí)會(huì)賦值為false
2)此時(shí)如果消費(fèi)者消費(fèi)速度慢矾策,這時(shí)產(chǎn)生了一個(gè)現(xiàn)象磷账,叫做背壓
。背壓?jiǎn)栴}即是外部的生產(chǎn)者和消費(fèi)者速度差造成的贾虽,此時(shí)我們需要暫停寫入.pause()
- 可寫流監(jiān)聽
drain
事件
1)消費(fèi)者完成消費(fèi)逃糟,可以觸發(fā)drain
事件,此時(shí)我們可以繼續(xù)向流中寫入數(shù)據(jù)蓬豁。執(zhí)行.resume()
- 觸發(fā)
pipe
事件绰咽,通知有流寫入 - 返回
dest
流,可以進(jìn)行鏈?zhǔn)秸{(diào)用
小記
簡(jiǎn)單的梳理了下stream pipe的原理和實(shí)現(xiàn)方式地粪。在現(xiàn)實(shí)應(yīng)用中諸如gulp取募,Browserify等工作流工具都使用了pipe。而stream更是應(yīng)用廣泛蟆技,文件玩敏、http斗忌、打包壓縮甚至console等都是用流實(shí)現(xiàn)的。