淺談node.js中的stream(流)

前言

什麼是流呢?看字面意思,咱們可能會想起生活中的水流,電流。
可是流不是水也不是電,它只是描述水和電的流動;因此說流是抽象的。
在node.js中流是一個抽象接口,它不關心文件內容,只關注是否從文件中讀到了數據,以及讀到數據以後的處理,接着看:html

1.流的概念

  • 流是一組有序的,有起點和終點的字節數據傳輸手段
  • 它不關心文件的總體內容,只關注是否從文件中讀到了數據;以及讀到數據以後的處理
  • 流是一個抽象接口,被node中的不少對象所實現。好比HTTP服務器request和response對象都是流,TCP服務器中的socket也是流。

看看官網的介紹:
圖片描述node

這裏說了「全部的流都是EventEmitter的實例」 因此流繼承了EventEmitter類。再來看流的類型:api

2.流的類型

3.流的數據模式

流中的數據有兩種模式,二進制模式和對象模式.緩存

  1. 二進制模式, 每一個分塊都是buffer或者string對象.
  2. 對象模式, 流內部處理的是一系列普通對象.
注意:
全部使用 Node.js API 建立的流對象都只能操做 strings 和 Buffer對象。可是,經過一些第三方流的實現,你依然可以處理其它類型的 JavaScript 值 (除了 null,它在流處理中有特殊意義)。 這些流被認爲是工做在 「對象模式」(object mode)。 在建立流的實例時,能夠經過 objectMode 選項使流的實例切換到對象模式。試圖將已經存在的流切換到對象模式是不安全的。
說了那麼多,如今開始寫流:

4.可讀流(createReadStream)

4.1 建立可讀流

圖片描述

這裏說說流程:安全

  • 首先可讀流會打開文件,觸發open事件
  • 接着開始讀取,瘋狂的觸發data事件
  • 而後讀完了,觸發end事件
  • 最後由於設置了autoClose爲true,自動關閉文件觸發close事件

能夠看到,data事件不斷的被觸發,當咱們想讀一下停一下時怎麼辦呢?
這裏就須要聊聊可讀流的兩種模式:服務器

4.2 可讀流的兩種模式

  • 可讀流事實上工做在下面兩種模式之一:flowing 和 paused
  • 在 flowing 模式下, 可讀流自動從系統底層讀取數據,並經過 EventEmitter 接口的事件儘快將數據提供給應用。
  • 在 paused 模式下,必須顯式調用 stream.read() 方法來從流中讀取數據片斷。

初始工做模式爲 paused 的 Readable 流,能夠經過下面三種途徑切換到 flowing 模式:socket

1. 監聽 'data' 事件
2. 調用 stream.resume() 方法

圖片描述

3.調用 stream.pipe() 方法將數據發送到 Writable
注意:
若是 Readable 切換到 flowing 模式,且沒有消費者處理流中的數據,這些數據將會丟失。 好比, 調用了 readable.resume() 方法卻沒有監聽 'data' 事件,或是取消了 'data' 事件監聽,就有可能出現這種狀況

在 paused 模式下,必須顯式調用 stream.read() 方法來從流中讀取數據片斷。
在可讀流'readable'事件裏咱們就必須調用stream.read()方法。函數

4.3 可讀流'readable'事件

這裏須要明白三點:
先建立一個1.txt
圖片描述ui

1.當我只要建立一個流 就會先把緩存區 填滿,等待着你本身消費
2.當你消費小於 最高水位線時 會自動添加highWaterMark這麼多數據
圖片描述spa

3.若是當前緩存區被清空後會再次觸發readable事件
圖片描述

4.4 readable原理圖

  • 用Readable建立對象readable後,便獲得了一個可讀流。
  • 若是實現_read方法,就將流鏈接到一個底層數據源。
  • 流經過調用_read向底層請求數據,底層再調用流的push方法將須要的數據傳遞過來。
  • 當readable鏈接了數據源後,下游即可以調用readable.read(n)向流請求數據,同時監聽readable的data事件來接收取到的數據。

圖片描述

5.可寫流(createWriteStream)

5.1 建立可寫流

5.1.1 write方法

圖片描述

5.1.2 end方法

代表接下來沒有數據要被寫入 Writable 經過傳入可選的 chunk 和 encoding 參數,能夠在關閉流以前再寫入一段數據 若是傳入了可選的 callback 函數,它將做爲 'finish' 事件的回調函數

圖片描述

5.1.3 drain方法

drain事件的觸發條件,必須知足兩個條件:
1.當前緩存區滿了,不能再寫了
2.緩存區滿了後被清空了,纔會觸發drain事件
圖片描述

6.pipe方法(管道)

咱們在開發中可能會遇到,要把可讀流讀出的數據須要放到可寫流中去寫入到文件裏面,這時就能夠用pipe方法

6.1 pipe的原理

pipe方法的原理很簡單,就是讀一點,寫一點,上代碼

let fs = require('fs');
let ws = fs.createWriteStream('./2.txt');
let rs = fs.createReadStream('./1.txt');
rs.on('data', data => {
    var flag = ws.write(data);
    if(!flag)
    rs.pause();
});
ws.on('drain', () => {
    rs.resume();
});
rs.on('end',  () => {
    ws.end();
});

6.2 pipe的用法

let from = fs.createReadStream('./1.txt');
let to = fs.createWriteStream('./2.txt');
from.pipe(to);

6.3 unpipe方法

readable.unpipe()方法將以前經過stream.pipe()方法綁定的流分離

let fs = require('fs');
let from = fs.createReadStream('./1.txt');
let to = fs.createWriteStream('./2.txt');
from.pipe(to);
setTimeout(() => {
    console.log('關閉向2.txt的寫入');
    from.unpipe(writable);
    console.log('手動關閉可寫流');
    to.end();
}, 1000);

7.自定義流

咱們能夠引入stream模塊,想實現什麼流 就繼承這個流。

7.1 自定義可讀流

  • 咱們能夠直接把供使用的數據push出去。
  • 當push一個null對象就意味着咱們想發出信號——這個流沒有更多數據了

圖片描述

7.2 自定義可寫流

爲了實現可寫流,咱們須要使用流模塊中的Writable構造函數。 咱們只需給Writable構造函數傳遞一些選項並建立一個對象。惟一須要的選項是write函數,該函數揭露數據塊要往哪裏寫。
圖片描述

7.3 實現雙工流(duplex)

有了雙工流,咱們能夠在同一個對象上同時實現可讀和可寫,就好像同時繼承這兩個接口。 重要的是雙工流的可讀性和可寫性操做徹底獨立於彼此。
net中的Socket就是一個duplex雙工流
圖片描述

8.結束語

說到這裏,我想你們應該大體瞭解了node.js裏面的流。以前說過在node裏流仍是很重要的,http裏的request和response都是流。在下一篇文章我會寫一個readStream和writeStream的簡單實現。本人水平有限,有錯誤的地方但願指出。

相關文章
相關標籤/搜索