初探Node中的stream

_shine發表於2018-02-02

#Stream流有以下四種型別:

  • Readable - 可讀操作
  • Writable - 可寫操作
  • Duplex - 可讀可寫操作
  • Transform - 操作被寫入資料,然後讀出結果

可讀流(Readable stream)

可讀流(Readable stream)介面是對你正在讀取的資料的來源的抽象。換句話說,資料來來自可讀流(Readable stream)不會分發資料,直到你表明準備就緒。 可讀流(Readable stream) 有2種模式: 流動模式(flowing mode) 和 暫停模式(paused mode). 流動模式(flowing mode)時,儘快的從底層系統讀取資料並提供給你的程式。 暫停模式(paused mode)時, 你必須明確的呼叫 stream.read() 來讀取資料。 暫停模式(paused mode) 是預設模式。 可以通過下面幾個方法,將流切換到流動模式(flowing mode)。

let fs = require('fs');
/**
 * 所有初始工作模式為 paused 的 Readable 流,可以通過下面三種途徑切換到 flowing 模式:
 監聽 'data' 事件
 呼叫 stream.resume() 方法
 呼叫 stream.pipe() 方法將資料傳送到 Writable
 */
let rs = fs.createReadStream('./1.txt',{
    highWaterMark:3
});
/*
269
 stream.emit('data', chunk);
    stream.read(0);
rs.on('data',function (data) {
    console.log(data);
});
rs.on('end',function () {
    console.log('end');
});*/
//當你監聽 readable事件的時候,會進入暫停模式
//當監聽readable事件的時候,可讀流會馬上去向底層讀取檔案,然後把讀到檔案的檔案放在快取區裡const state = this._readableState;
//self.read(0); 只填充快取,但是並不會發射data事件,但是會發射stream.emit('readable');事件
//this._read(state.highWaterMark); 每次呼叫底層的方法讀取的時候是讀取3個位元組
rs.on('readable',function(){
    //length就是指得快取區資料的大小
    // state.length +=  chunk.length;==3
    console.log(rs._readableState.length);
    //read如果不加參數列示讀取整個快取區資料
    //讀取一個欄位,如果可讀流發現你要讀的位元組小於等於快取位元組大小,則直接返回
    let ch = rs.read(1);
    console.log(ch);
    console.log(rs._readableState.length);
   /* ch = rs.read(1);
    console.log(ch);
    console.log(rs._readableState.length);*/
    //當你讀完指定的位元組後,如果可讀流發現剩下的位元組已經比最高水位線小了。則會立馬再次讀取填滿 最高水位線
    setTimeout(function(){
        console.log(rs._readableState.length);
    },200)
});
複製程式碼

#可寫流(Writable stream )

這個方法向底層系統寫入資料,並在資料處理完畢後呼叫所給的回撥。返回值表示你是否應該繼續立即寫入。如果資料要快取在內部,將會返回false。否則返回 true。返回值僅供參考。即使返回 false,你也可能繼續寫。但是寫會快取在記憶體裡,所以不要做的太過分。最好的辦法是等待drain 事件後,再寫入資料。

let fs = require('fs');
let ws = fs.createWriteStream('2.txt',{
    flags:'w',
    mode:0o666,
    start:0,
    highWaterMark:3
});
let count = 9;
function write(){
 let flag = true;//快取區未滿
    //寫入方法是同步的,但是寫入檔案的過程 非同步的。在真正寫入檔案後還會執行我們的回撥函式
 while(flag && count>0){
     console.log('before',count);
     flag = ws.write((count)+'','utf8',(function (i) {
         return ()=>console.log('after',i);
     })(count));
     count--;
 }
}
write();//987
//監聽快取區清空事件
ws.on('drain',function () {
    console.log('drain');
    write();//654 321
});
ws.on('error',function (err) {
    console.log(err);
});
//如果已經不再需要寫入了,可以呼叫end方法關閉寫入流,一旦呼叫end方法之後則不能再寫入
ws.end();
//write after end
//
ws.write('x');
複製程式碼

#雙工流(Duplex streams) 雙工流(Duplex streams)是同時實現了 Readable and Writable 介面。用法詳見下文

let {Duplex} = require('stream');
let index = 0;
let s = Duplex({
    read(){
        if(index++<3)
          this.push('a'); 
          else 
       this.push(null);   
    },
    write(chunk,encoding,cb){
       console.log(chunk.toString().toUpperCase());
       cb();
    }
});
//process.stdin 標準輸入流
//proces.stdout標準輸出流
process.stdin.pipe(s).pipe(process.stdout);
複製程式碼

#轉換流(Transform streams) 它的輸出是從輸入計算得來。 它實現了Readable 和 Writable 介面. 用法詳見下文.

let {Transform}  = require('stream');
//轉換流是實現資料轉換的
let t = Transform({
    transform(chunk,encoding,cb){
        this.push(chunk.toString().toUpperCase());
        cb();
    }
});
process.stdin.pipe(t).pipe(process.stdout);
複製程式碼
let {Transform} = require('stream');
let fs = require('fs');
let rs = fs.createReadStream('./user.json');
//普通流裡的放的是Buffer,物件流裡放的物件
let toJSON = Transform({
    readableObjectMode:true,//就可以向可讀流裡放物件
    transform(chunk,encoding,cb){
        //向可讀流裡的快取區裡放
      this.push(JSON.parse(chunk.toString()));
    }
});
let outJSON = Transform({
    writableObjectMode:true,//就可以向可讀流裡放物件
    transform(chunk,encoding,cb){
      console.log(chunk);
      cb();
    }
});
rs.pipe(toJSON).pipe(outJSON);
複製程式碼

相關文章