nodejs學(xué)習(xí)筆記Stream(流)

什么流

通俗的說就是一種道盏,有起點和終點的字節(jié)數(shù)據(jù)傳輸手段因苹,把數(shù)據(jù)從一個地方傳到另一個地方鞭呕。
流(Stream)是一個抽象接口蛤育,可讀、可寫或兼具兩者的葫松。并且所有流都是 EventEmitter 的實例瓦糕。
基于流實現(xiàn)的工具 webpack glup,比如HTTP 服務(wù)器request和response 腋么,(TCP sockets)咕娄,標(biāo)準(zhǔn)輸出(process.stdout)等等對象都是流。

可讀流 (Readable Stream)

可讀流存在兩種工作模式 流動模式(flowing)暫停模式 (paused)
下面介紹一下兩個模式的特點 :

暫停模式

可讀流在默認(rèn)狀態(tài)就是暫停模式 珊擂,不監(jiān)聽readable也會默認(rèn)先打開文件圣勒,打開文件后會調(diào)用一次read(0)方法
我們讀取數(shù)據(jù)的時候如果文件過大,我們選擇一點一點讀取摧扇,讀取字節(jié)為highWaterMark的值圣贸,放到緩存區(qū)中。
在暫停模式中扛稽,我們監(jiān)聽readable事件吁峻,可讀流會馬上去向底層讀取文件,然后把讀到文件的文件放在緩存區(qū)里const state = this._readableState;,同過read()方法來消費數(shù)據(jù)用含。
看下面一個暫停模式的例子:

let fs = require('fs');
let rs = fs.createReadStream('1.txt',{
    highWaterMark:3,
    encoding:'utf8'
});
rs.on('readable',function () {
    let char = rs.read(1);
    console.log(char);
});
read() 方法

readable事件中橙困,read回向可讀流請求讀取n個字節(jié)的數(shù)據(jù),根據(jù)n值的不同會有下面幾種情況:

  • n = undefined ; 即不傳參數(shù)耕餐,此時文件會不斷讀取hwm(hignWaterMark)字節(jié)凡傅,并且不斷觸發(fā)readable事件,讀取緩存區(qū)(hwm的值)肠缔,如果沒設(shè)置hwm大小夏跷,讀取默認(rèn)大小64k。
  • n = 0 ; 可讀流返回一個null 并且不會消費任何數(shù)據(jù)
  • 0 < n < hwm ; 此時n小于最高水位線執(zhí)行底層的 _read 方法明未,從數(shù)據(jù)源中讀取hwm大小的數(shù)據(jù)填充到緩存區(qū)內(nèi)槽华。并且下次讀取字節(jié)為( hwm-n) + hwm 個字節(jié)
  • n > hwm ; read 方法會先返回null,然后從數(shù)據(jù)源處讀取hwm大小的數(shù)據(jù)加入緩沖區(qū)趟妥,并判斷緩沖區(qū)內(nèi)數(shù)據(jù)大小是否大于或等于n猫态,如果是則返回數(shù)據(jù),否則會再次返回null并讀取n大小的數(shù)據(jù)披摄。
readable 事件

在 readable 事件表示流中有數(shù)據(jù)可以被讀取 有兩種情況會被觸發(fā):

  • 緩存區(qū)為空亲雪,或者或 緩存區(qū)大小 - 可讀大小 < hwm 時,第一次緩存區(qū)大小為hwm第二次為緩存去大小為 剩余緩存大小 + hwm
  • 當(dāng)文件讀完時疚膊,會自動觸發(fā) readable 事件

流動模式

開啟流動模式的常用方法為兩種:監(jiān)聽data 或者使用pipe(管道)方法义辕,下面兩個例子減少一下這兩中方法。

let fs = require('fs');
let rs = fs.createReadStream('./1.txt',{
    highWaterMark:3
});
rs.setEncoding('utf8');
rs.on('data',function (data) {
    console.log(data);
    rs.pause();//暫停讀取和發(fā)射data事件
    setTimeout(function(){
        rs.resume();//恢復(fù)讀取并觸發(fā)data事件
    },2000);
});

當(dāng)監(jiān)聽data 事件后 寓盗,可讀流會不斷從數(shù)據(jù)源去除hwm大小的數(shù)據(jù)灌砖,并向data事件發(fā)送這些數(shù)據(jù), 我們當(dāng)然和以使用stream.pause()手動將流切換到暫停模式傀蚌,否則該過程將持續(xù)下去基显,直到讀到數(shù)據(jù)源的結(jié)束位置。

如果數(shù)據(jù)消費的速度小于數(shù)據(jù)生產(chǎn)的速度的話該怎么辦呢善炫,引出了跟高級的方法pipe它就像一個管道撩幽,比如我們一邊讀取文件,一邊把讀取的數(shù)據(jù)寫入另一個文件销部,這樣寫的速度會跟不上讀取的速度摸航,就相當(dāng)于制跟,消費小于生產(chǎn)的情況舅桩,如果寫的慢我們可讀流就會停下來,始終保持讀寫在一個頻率上雨膨。

let fs = require('fs');
let rs = fs.createReadStream('1.txt',{
    encoding:'utf8'
});
let ws = fs.createWriteStream('2.txt',{
    encoding:'utf8'
});
rs.pipe(ws);

pipe 方法返回一個 readable 對象擂涛,這意味著我們可以使用鏈?zhǔn)讲僮鲗?shù)個流連接在一起,管道一旦被接上,數(shù)據(jù)將持續(xù)不斷的從可讀流寫入可寫流撒妈。想要終止這個過程恢暖,只能使用 stream.unpipe() 來取消管道連接。

上面?zhèn)€兩個例子狰右,我們不僅理解了流動模式杰捂,還可以發(fā)現(xiàn)其實流動模式 和 暫停模式是可以切換的,下面總結(jié)一下模式切換的方法:

暫停模式切換到流動模式:
  • 監(jiān)聽 data 事件
  • 調(diào)用 stream.resume() 方法
  • 調(diào)用 stream.pipe() 方法將數(shù)據(jù)發(fā)送到可寫流中
流動模式切換到暫停模式:
  • 如果不存在管道目標(biāo)(pipe destination)棋蚌,可以通過調(diào)用 stream.pause() 方法實現(xiàn)嫁佳。
  • 如果存在管道目標(biāo),可以通過取消 data 事件監(jiān)聽谷暮,并調(diào)用 stream.unpipe() 方法移除所有管道目標(biāo)來實現(xiàn)蒿往。

可寫流 (Writable stream)

可讀流的默認(rèn)緩存空間是64k,可寫流的默認(rèn)緩存空間為16k湿弦,可寫流當(dāng)時是向目標(biāo)文件些數(shù)據(jù)的瓤漏,下面同樣通過代碼了解:

let fs = require('fs');
let ws = fs.createWriteStream('2.txt',{
    flag:'w',
    mode:0o666,
    highWaterMark:3,
    encoding:'utf8'
});
let count = 9;
function write(){
    let flag = true;
    while(flag && count>0){
        flag = ws.write(count-- +'');
    }
}
write();
ws.on('drain',function () {
    console.log('drain');
    write();
});

面這段代碼展示了一個可寫流實例的幾個基本的事件和方法,下面我們來逐一介紹:

write()

這個方法的作用是向可寫流寫入數(shù)據(jù)颊埃,它的類型必須是字符串或者Buffer蔬充,同時方法返回值為布爾值,當(dāng)前我們的緩存區(qū)大小為hwm3個字符班利,我們一個一個字符的寫娃惯,當(dāng)寫到第三個時,緩存區(qū)滿了肥败,此時返回false趾浅。
一旦我們確認(rèn)方法返回了false后,應(yīng)該立刻停止調(diào)用 write 方法馒稍,直到緩沖器中的數(shù)據(jù)被清空為止皿哨。當(dāng)然,即使方法返回了false纽谒,你實際上也可以繼續(xù)使用 write 方法寫入數(shù)據(jù)证膨。node會將你寫入的數(shù)據(jù)全部緩存起來,直到超過了能使用的最大內(nèi)存鼓黔。

end()

end 方法的作用是關(guān)閉流央勒,它可以傳入三個可選的參數(shù)。chunk 和 encoding 是在關(guān)閉可寫流前希望最后寫入的數(shù)據(jù)及其對應(yīng)的編碼澳化。如果傳入 callback崔步,這個 callback 會作為 finish 事件的回調(diào)函數(shù)觸發(fā)

drain事件

在可寫流的緩沖區(qū)超過hwm的條件下,會觸發(fā)drain事件缎谷,提示使用者井濒,先不要在寫了,內(nèi)存滿了,注意這個事件觸發(fā)的前提瑞你,即write 方法返回了false后清空緩沖區(qū)才會觸發(fā) drain 事件

注意:建議在 write 方法返回false時停止寫入數(shù)據(jù)酪惭,在 drain 事件的回調(diào)中再次開始寫入,這樣可以更好的控制緩沖區(qū)的大小者甲,避免發(fā)生內(nèi)存泄漏問題春感。

close事件

close 如文件系統(tǒng)被關(guān)閉時。當(dāng) close 事件被觸發(fā)后虏缸,可寫流將不會再觸發(fā)其他事件甥厦。值得注意的是,不是所有可寫流都會觸發(fā) close 事件寇钉。

可寫流可讀流大概內(nèi)容已經(jīng)說完了刀疙,下面配上一張圖再總結(jié)一下流的原理:
image.png
  • 當(dāng)建立一個可讀流的時候,可讀流默認(rèn)會監(jiān)聽readable 事件扫倡,此時的read(n) n=0;不會消費任何數(shù)據(jù)
  • 當(dāng)我們主動監(jiān)聽readable事件時谦秧,調(diào)用read方法消費數(shù)據(jù),當(dāng)緩存區(qū)為空時撵溃,會再次觸發(fā)readable事件疚鲤,直到你讀完了源文件的所有數(shù)據(jù)。
  • 當(dāng)我們監(jiān)聽 data的時候缘挑,通過data 事件回調(diào)進(jìn)行消費集歇,這個過程不會停止,直到全部讀取完成语淘。當(dāng)然诲宇,在這個過程中你隨時可以通過stream.pause()方法暫停它。
  • 接下來創(chuàng)建一個可寫流惶翻,并調(diào)用write方法來消費我們的數(shù)據(jù)姑蓝,但是因為寫入的速度較慢,如果當(dāng)前寫入還在進(jìn)行吕粗,而你又調(diào)用了write方法纺荧,node會將你要寫入的數(shù)據(jù)緩存在一個緩存區(qū)中,等到文件寫入完畢會從緩存區(qū)中取出數(shù)據(jù)颅筋,繼續(xù)寫入宙暇。
  • write 有一個布爾類型的返回值,如果寫入過快议泵,緩存區(qū)滿了之后就會返回false占贫。
  • 當(dāng)緩存區(qū)內(nèi)容完全寫入清空口,這是會調(diào)用drain事件肢簿,我們可以在他的回調(diào)中靶剑,繼續(xù)寫入文件蜻拨,執(zhí)行write()

如有對不的地方池充,不吝賜教

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桩引,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子收夸,更是在濱河造成了極大的恐慌坑匠,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卧惜,死亡現(xiàn)場離奇詭異厘灼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)咽瓷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門设凹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人茅姜,你說我怎么就攤上這事闪朱。” “怎么了钻洒?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵奋姿,是天一觀的道長。 經(jīng)常有香客問我素标,道長称诗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任头遭,我火速辦了婚禮寓免,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘计维。我一直安慰自己再榄,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布享潜。 她就那樣靜靜地躺著困鸥,像睡著了一般。 火紅的嫁衣襯著肌膚如雪剑按。 梳的紋絲不亂的頭發(fā)上疾就,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音艺蝴,去河邊找鬼猬腰。 笑死,一個胖子當(dāng)著我的面吹牛猜敢,可吹牛的內(nèi)容都是我干的姑荷。 我是一名探鬼主播盒延,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鼠冕!你這毒婦竟也來了添寺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤懈费,失蹤者是張志新(化名)和其女友劉穎计露,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體憎乙,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡票罐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泞边。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片该押。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖阵谚,靈堂內(nèi)的尸體忽然破棺而出蚕礼,到底是詐尸還是另有隱情,我是刑警寧澤椭蹄,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布闻牡,位于F島的核電站,受9級特大地震影響绳矩,放射性物質(zhì)發(fā)生泄漏罩润。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一翼馆、第九天 我趴在偏房一處隱蔽的房頂上張望割以。 院中可真熱鬧,春花似錦应媚、人聲如沸严沥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽消玄。三九已至,卻和暖如春丢胚,著一層夾襖步出監(jiān)牢的瞬間翩瓜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工携龟, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留兔跌,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓峡蟋,卻偏偏與公主長得像坟桅,于是被迫代替她去往敵國和親华望。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容