說說node中可讀流和可寫流

前言

    nodejs中大量的api與流有關,曾經看到公司的一些大神的node代碼,實現一個接口只須要pipe一下另外一個java接口就能夠了。簡單的一行代碼實在讓人困惑。做爲小白的本身一臉懵逼卻又不敢問,由於根本不知道從何問起。如今終於經過學習,也能對流說出個123,但願和你們共同交流。html

流簡介

    流分爲緩衝模式和對象模式,緩衝模式只能處理buffer或字符串,對象模式能夠處理js對象。流又分爲四種類型:可讀流、可寫流、雙工流和轉換流。後兩種實際上是對可讀和可寫流的應用。因此我想先聊聊可讀流和可寫流。java

可讀流

可讀流有兩種模式,並隨時能夠轉換,咱們能夠經過監聽可讀流的事件來操做它。node

兩種模式(引用自node中文網的描述):
一、流動模式:可讀流自動讀取數據,經過EventEmitter接口的事件儘快將數據提供給應用。
二、暫停模式:必須顯式調用stream.read()方法來從流中讀取數據片斷。api

暫停模式切換到流動模式的api有:
一、監聽「data」事件
二、調用 stream.resume()方法
三、調用 stream.pipe()方法將數據發送到可寫流
緩存

流動模式切換到暫停模式的api有:
一、若是不存在管道目標,調用stream.pause()方法
二、若是存在管道目標,調用 stream.unpipe()並取消'data'事件監聽
bash

可讀流事件:'data','readable','error','close','end'函數

可寫流

可寫流相對較爲簡單,咱們也能夠經過監聽它的事件來操做它。學習

可寫流事件: 'close','drain','error','finish','pipe','unpipe'ui

舉個栗子

    我以一個簡單的例子描述一個流最多見的場景,談談對這個過程的理解。例子就是:我要讀取一個文件,而後把它的內容寫到另外一個文件(固然是用「流」的api,而不是用‘fs’模塊的api)。 spa

接下來我解釋一下這張圖:
    如上圖,當咱們建立了一個可讀流的時候,readable._readableState.flowing屬性默認爲null,這時咱們有兩種選擇:
    一、監聽‘readable’事件,這時可讀流會讀取64k(能夠在建立可讀流時,經過option參數中的highWaterMark更改)數據到流的緩存區中,等待你用read方法去讀取並消費數據,當你用read方法讀了64k數據以後,會再次觸發readable事件,直到你讀完了源文件的全部數據。記住,之因此叫暫停模式是由於若是你不調用read方法,代碼永遠會停在這裏,什麼事情也不會發生了。
    二、若是你選擇監聽‘data’事件,可讀流會直接讀取64k數據並經過‘data’事件的回掉函數提供給你消費,而且這個過程不會中止,若是源文件中有不少數據,會不停的觸發‘data’事件,直到所有讀取完成。固然,在這個過程當中你隨時能夠經過stream.pause()方法暫停它。
    那麼,這兩種模式有什麼區別呢?在我理解,若是你不須要對數據進行精確控制,首先選擇流動模式,由於它的效率更高。若是須要對流的過程進行精確控制則能夠選擇暫停模式。也就是說暫停模式是流更高級一些的用法。其實官方建議咱們儘可能不要手動去操做流,若是能夠,儘可能使用pipe方法。
    接下來,不論咱們以哪一種方式讀到了文件中的數據,這時咱們均可以建立一個可寫流並調用可寫流的write方法來消費讀到的數據。調用write方法會向文件中寫入數據,可是由於寫入的速度較慢,若是當前寫入還在進行,而你又調用了write方法,node會將你要寫入的數據緩存在一個緩存區中,等到文件寫入完畢會從緩存區中取出數據,繼續寫入。
    write方法擁有一個布爾類型的返回值,用來表示目前是否還能夠繼續調用write方法寫入內容。若是返回false,咱們應當中止讀取數據以免消耗過多內存。那麼何時會返false呢?就是當緩存區的大小大於16k(能夠在建立可讀流時,經過option參數中的highWaterMark更改)時。
    緩存區滿後,文件寫入一直在進行,不一下子會把緩存區的內容所有寫入,緩存區處於清空狀態,這時會觸發可寫流的‘drain’事件,這時咱們能夠繼續向文件寫入數據了。注意:若是緩存區從未滿過,‘drain’事件永遠也不會觸發。

那麼這張圖對應到代碼是什麼樣的呢:

let fs = require('fs');
//建立可讀可寫流
let rs = fs.createReadStream('./1.txt');
let ws = fs.createWriteStream('./2.txt');
//監聽‘data’事件,開啓流動模式
rs.on('data',function (data) {
    //對應圖中的可寫流truefalse
    let flag = ws.write(data);
    if(!flag){
        //若是可寫流返回false,咱們應當中止讀取,以免消耗過多內存
        rs.pause();
    }
});
//對應圖中的drain事件
ws.on('drain',function () {
    //從新開啓流動模式
    rs.resume();
});

//使用可讀流的暫停模式
function read() {
    let data = rs.read()
    let flag = ws.write(data);
    if(flag){
       read()
    }
}
rs.on('readable',function(){
    read()
})

ws.on('drain',function () {
    read()
});


複製代碼

結尾

    可讀和可寫流的用法和api還有不少,這裏只是簡單的梳理了一下基本過程,若是有描述不許確的地方還請你們在評論區多指正。

參考資料

  • https://nodejs.org/dist/latest-v8.x/docs/api/stream.html
  • http://nodejs.cn/api/
相關文章
相關標籤/搜索