簡體   English   中英

如何在 node.js 可讀流中調用異步函數

[英]How to call an asynchronous function inside a node.js readable stream

這是自定義可讀流的實現的簡短示例。 該類稱為 MyStream。 流從目錄中獲取文件/文件夾名並將值推送到數據事件。

為了比較我實現(在這個例子中)兩種不同的方式/功能。 一個是同步的,另一個是異步的。 構造函數的第二個參數讓你決定使用哪種方式(異步為真,同步為假。

readcounter 計算方法 _read 被調用的次數。 只是為了提供反饋。

var Readable = require('stream').Readable;
var util = require('util');
var fs = require('fs');
util.inherits(MyStream, Readable);

function MyStream(dirpath, async, opt) {
  Readable.call(this, opt);
  this.async = async;
  this.dirpath = dirpath;
  this.counter = 0;
  this.readcounter = 0;
}

MyStream.prototype._read = function() {
  this.readcounter++;
  if (this.async === true){
    console.log("Readcounter: " + this.readcounter);
    that = this;
    fs.readdir(this.dirpath,function(err, files){
      that.counter ++;
      console.log("Counter: " + that.counter);
      for (var i = 0; i < files.length; i++){
        that.push(files[i]);
      }
      that.push(null);
    });
  } else {
    console.log("Readcounter: " + this.readcounter);
    files = fs.readdirSync(this.dirpath)
    for (var i = 0; i < files.length; i++){
      this.push(files[i]);
    };
    this.push(null);
  }
};
//Instance for a asynchronous call
mystream = new MyStream('C:\\Users', true);
mystream.on('data', function(chunk){
  console.log(chunk.toString());
});

同步方式按預期工作,但是當我異步調用它時,發生了一些有趣的事情。 每次通過that.push(files[i])推送文件名時,都會再次調用 _read 方法。 當第一個異步循環完成並且that.push(null)定義流的結尾時,這會導致錯誤。

我用來測試的環境:節點 4.1.1,電子 0.35.2。

我不明白為什么 _read 被如此頻繁地調用以及為什么會發生這種情況。 也許這是一個錯誤? 或者有什么我現在看不到的東西。 有沒有辦法使用異步函數構建可讀流? 異步推送塊會非常酷,因為它是非阻塞流方式。 特別是當您有大量數據時。

_read在“讀者”需要數據時被調用,它通常在您推送數據之后發生。

我在直接實現_read遇到了同樣的“問題”,所以現在我編寫了一個返回流對象的函數。 它工作得很好,無法從我的流中“拉出”數據,當我決定數據時,數據是可用的/推送的。 用你的例子,我會這樣做:

var Readable = require('stream').Readable;
var fs = require('fs');

function MyStream(dirpath, async, opt) {
  var rs = new Readable();
  // needed to avoid "Not implemented" exception
  rs._read = function() { 
    // console.log('give me data!'); // << this will print after every console.log(folder);
  };

  var counter = 0;
  var readcounter = 0;

  if (async) {
    console.log("Readcounter: " + readcounter);
    fs.readdir(dirpath, function (err, files) {
      counter++;
      console.log("Counter: " + counter);
      for (var i = 0; i < files.length; i++) {
        rs.push(files[i]);
      }
      rs.push(null);
    });
  } else {
    console.log("Readcounter: " + readcounter);
    files = fs.readdirSync(dirpath)
    for (var i = 0; i < files.length; i++) {
      rs.push(files[i]);
    };
    rs.push(null);
  }

  return rs;
}

var mystream = MyStream('C:\\Users', true);
mystream.on('data', function (chunk) {
  console.log(chunk.toString());
});

它不會直接回答您的問題,但它是獲取工作代碼的一種方式。

從節點 10 開始修復

https://github.com/nodejs/node/issues/3203

如果我的理解是正確的,在 Node 10 之前,異步_read()實現只需要使用數據調用this.push()一次並創建自己的緩沖區,以便將this.push()延遲到下一個_read()調用。

const {Readable} = require('stream');
let i = 0;
const content_length = 5;
let content_read = 0;

const stream = new Readable({
  encoding: 'utf8',
  read() {
    console.log('read', ++i);
    const icopy = i;
    setTimeout(() => {
      for (let a=1; a<=3; a++) {
        this.push(icopy+':'+a);
      }
      content_read++;
      if (content_read == content_length) {
        console.log('close');
        this.push(null);
      }
    }, Math.floor(Math.random()*1000));
  },
});

stream.on('data', (data) => {
  console.log(data);
});

節點 8.17.0 :

read 1
1:1
read 2
1:2
read 3
1:3
read 4
2:1
read 5
2:2
read 6
2:3
read 7
6:1
read 8
6:2
read 9
6:3
read 10
9:1
read 11
9:2
read 12
9:3
read 13
12:1
read 14
12:2
read 15
12:3
read 16
close
events.js:183
      throw er; // Unhandled 'error' event
      ^

Error: stream.push() after EOF

節點 10.24.1:

read 1
1:1
1:2
1:3
read 2
2:1
2:2
2:3
read 3
3:1
3:2
3:3
read 4
4:1
4:2
4:3
read 5
5:1
5:2
5:3
close

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM