首頁  >  文章  >  web前端  >  Nodejs中什麼是可寫流?怎麼使用

Nodejs中什麼是可寫流?怎麼使用

青灯夜游
青灯夜游轉載
2020-11-23 17:49:303265瀏覽

Nodejs中什麼是可寫流?怎麼使用

相關推薦:《nodejs 教學

#什麼是可寫流

可寫流是對資料流向設備的抽象,用來消費上游流過來的數據,透過可寫入流程式可以把資料寫入設備,常見的是本地磁碟檔案或TCP、HTTP 等網路回應。

看一個之前用過的例子

process.stdin.pipe(process.stdout);

*process.stdout* 是一個可寫流,程式把可讀流process.stdin 傳過來的資料寫入的標準輸出設備。在了解了可讀流的基礎上理解可寫流非常簡單,流就是有方向的數據,其中可讀流是數據源,可寫流是目的地,中間的管道環節是雙向流。

可寫入流使用

呼叫可寫流實例的**write() **方法就可以把資料寫入可寫流

const fs = require('fs');
const rs = fs.createReadStream('./w.js');
const ws = fs.createWriteStream('./copy.js');

rs.setEncoding('utf-8');
rs.on('data', chunk => {
  ws.write(chunk);
});

前面提到過監聽了可讀流的data 事件就會使可讀流進入流動模式,我們在回呼事件裡呼叫了可寫流的write() 方法,這樣資料就被寫入了可寫流抽象的裝置中,也就是目前目錄下的copy.js 檔案。

write() 方法有三個參數

  • chunk# {String| Buffer},表示要寫入的資料
  • encoding 當寫入的資料是字串的時候可以設定編碼
  • callback 資料被寫入之後的回呼函數

自訂可寫流

和自訂可讀流類似,簡單的自訂可寫流只需要兩個步驟

  1. 繼承stream 模組的Writable 類別
  2. 實作_write() 方法

#我們來實作一個簡單的可寫流,把傳入可寫流的資料轉成大寫之後輸出到標準輸出設備(比較好的例子可能是寫入本地磁碟文件,但涉及過多的fs 操作,比較麻煩,偷個懶。寫入標準輸出設備也是一種寫入行為)

const Writable = require('stream').Writable

class OutputStream extends Writable {
    _write(chunk, enc, done) {
        // 转大写之后写入标准输出设备
        process.stdout.write(chunk.toString().toUpperCase());
        // 此处不严谨,应该是监听写完之后才调用 done
        process.nextTick(done);
    }
}

module.exports = OutputStream;

和最終可寫流暴露出來的write() 方法一樣, _write() 方法有三個參數,作用類似

  • chunk 寫入的數據,大部分時候是buffer,除非decodeStrings 被設定為false
  • encoding 如果資料是字串,可以設定編碼,buffer 或object 模式會忽略
  • #callback 資料寫入後的回呼函數,可以通知流傳傳入下一個資料;當出現錯誤的時候也可以設定一個error 參數

當然其實還有一個_writev() 方法可以實現,這個方法只被滯留的寫入佇列調用,可以不實現。

實例化可寫流

有了可寫流的類別之後我們可以實例化使用了,實例化可寫流的時候有幾個option 可選,了解一下可以幫助我們理解後面要用的知識

  • objectMode預設是false, 設定成true 後writable.write() 方法除了寫入string和buffer 外,還可以寫入任意JavaScript 物件。很有用的一個選項,後面介紹transform 流的時候詳細介紹
  • highWaterMark每次最多寫入的資料量, Buffer 的時候預設值16kb,objectMode時預設值16
  • decodeStrings是否把傳入的資料轉成Buffer,預設是true

這樣我們就更清楚的知道_write() 方法傳入的參數的意義了,而且對後面介紹back pressure 機制的理解很有幫助。

事件

和可讀流一樣,可寫流也有幾個常用的事件,有了可讀流的基礎,理解起來比較簡單

  • pipe 當可讀流呼叫pipe() 方法向可寫流傳輸資料的時候會觸發可寫流的pipe 事件
  • unpipe 當可讀流呼叫unpipe() 方法移除資料傳遞的時候會觸發可寫流的unpipe 事件

這兩個事件用於通知可寫流數據將要到來和將要被切斷,在通常情況下使用的很少。

writeable.write() 方法是有一個bool 的回傳值的,前面提到了highWaterMark,當要求寫入的資料大於可寫流的highWaterMark的時候,資料不會一次寫入,有一部分資料滯留,這時候writeable.write() 就會回傳false,如果可以處理完就會回傳true

drain 當先前存在滯留數據,也就是writeable.write() 回傳false,經過一段時間的消化,處理完了積壓數據,可以繼續寫入新數據的時候觸發(drain 的本意即為排水、枯竭,挺形象的)

除了 write() 方法可写流还有一个常用的方法 end(),参数和 write() 方法相同,但也可以不传入参数,表示没有其它数据需要写入,可写流可以关闭了。

finish 当调用 writable.end() 方法,并且所有数据都被写入底层后会触发 finish 事件

同样出现错误后会触发 error 事件

back pressure

了解了这些事件,结合上之前提到的可读流的一些知识,我们就能探讨一些有意思的话题了。在最开始我们提到过用流相对于直接操作文件的好处之一是不会把内存压爆,那么流是怎么做到的呢?

最开始我们可能会想到因为流不是一次性把所有数据载入内存处理,而是一边读一边写。但我们知道一般读取的速度会远远快于写入的速度,那么 pipe()  方法是怎么做到供需平衡的呢?

回忆一些基础知识,我们自己来实现一下 pipe() 方法的核心原理

  1. 可读流有流动和暂停两种模式,可以通过 **pause() resume() **方法切换
  2. 可写流的 **write() **方法会返回是否能处理当前的数据,每次可以处理多少是 hignWatermark 决定的
  3. 当可写流处理完了积压数据会触发 drain 事件

我们可以利用这三点来做到数据读取和写入的同步,还是使用之前的例子,但为了使消费速度降下来,我们各一秒再通知完成

class OutputStream extends Writable {
    _write(chunk, enc, done) {
        // 转大写之后写入标准输出设备
        process.stdout.write(chunk.toString().toUpperCase());
        // 故意延缓通知继续传递数据的时间,造成写入速度慢的现象
        setTimeout(done, 1000);
    }
}

我们使用一下自定义的两个类

const RandomNumberStream = require('./RandomNumberStream');
const OutputStream = require('./OutputStream');

const rns = new RandomNumberStream(100);
const os = new OutputStream({
    highWaterMark: 8 // 把水位降低,默认16k还是挺大的
});

rns.on('data', chunk => {
    // 当待处理队列大于 highWaterMark 时返回 false
    if (os.write(chunk) === false) { 
        console.log('pause');
        rns.pause(); // 暂停数据读取
    }
});

// 当待处理队列小于 highWaterMark 时触发 drain 事件
os.on('drain', () => {
    console.log('drain')
    rns.resume(); // 恢复数据读取
});

结合前面的三点和注释很容易看懂上面代码,这就是 pipe() 方法起作用的核心原理。数据的来源的去向我们有了大概了解,后面可以开始介绍数据的加工

  • duplex
  • transform

更多编程相关知识,请访问:编程学习课程!!

以上是Nodejs中什麼是可寫流?怎麼使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除