簡體   English   中英

Node.js可寫流完成未在類中觸發

[英]Node.js Writable Stream Finish Not Firing In Class

我正在嘗試創建一個可以在我的應用程序的其他腳本中使用的CSV類。 實例化CSV類后,它將創建可寫流到用戶指定的文件。 寫入和銷毀方法似乎可行,但是我無法弄清楚一旦數據完成寫入文件后如何讓類上的'writeEnd'成員執行。

“ writeEnd”成員變量是一個應由用戶覆蓋的函數。 例如,這是一個文件,我在其中測試類的功能,並將“ writeEnd”重寫為我自己選擇的功能。

PS:請在底部以粗體顯示該問題!

const CSV = require('./shared/classes/csv');
const csv = new CSV(__dirname);

csv.writeEnd = () => {
  console.log('Finished!');
};

for (let i = 0; i < 1000000; i++) {
  csv.write('Hello World.');
}

我希望“完成!” 記錄到控制台,但該功能根本不會觸發。 我希望我做錯了某人可以很容易地抓住的東西。

供您參考,這是未修改的類文件:

const { createWriteStream } = require('fs');
const { Readable } = require('stream');

/**
 * @class CSV
 */
module.exports = class CSV {
  constructor(path) {
    this.readStream = new Readable({ read() {} });
    this.writeStream = createWriteStream(`${path}/csv/data.csv`);
    this.readStream.pipe(this.writeStream);

    this.writeEnd = () => {};
  }

  /**
   * @method write
   * @param {any} data
   */
  write(data) {
    this.readStream.push(`${data}\n`);
  }

  /**
   * @method destroy
   */
  destroy() {
    this.readStream.destroy();
    this.writeStream.destroy();
  }
};

以下是我失敗的嘗試之一:

/**
 * @class CSV
 */
module.exports = class CSV {
  constructor(path) {
    this.readStream = new Readable({ read() {} });
    this.writeStream = createWriteStream(`${path}/csv/data.csv`);
    this.readStream.pipe(this.writeStream);

    // I'm wondering if this executes immediately because no writing is taking place
    // during instantiation
    this.writeStream.on('finish', this.writeEnd);
    this.writeEnd = () => {};
  }

  /**
   * @method write
   * @param {any} data
   */
  write(data) {
    this.readStream.push(`${data}\n`);
  }

  /**
   * @method destroy
   */
  destroy() {
    this.readStream.destroy();
    this.writeStream.destroy();
  }
};

我想知道我是否真的需要在readStream第一次將數據推送到它時才真正偵聽,然后設置“完成”回調?

問題在於,永遠不會調用自定義/重寫的writeEnd方法,因為事件發射器會保留對原始處理程序(即您在構造函數中設置的函數)的引用: this.writeEnd = () => {};

最簡單的方法是允許將回調函數傳遞給CSV -class的構造函數,並將其用作finish -handler。 考慮以下簡單示例:

const EventEmitter = require("events").EventEmitter;

class CSV {

    constructor(customWriteEndCb) {
        this.writeEnd = () => {
            console.log("in original writeEnd");
        };
        this.writeEnd = customWriteEndCb || this.writeEnd;
        this.writeStream = new EventEmitter();
        this.writeStream.on('finished', this.writeEnd);
    }

    testMe() {
        this.writeStream.emit('finished');
    }

}

const customWriteEnd = () => {
    console.log("in custom writeEnd")
}

const csv = new CSV(customWriteEnd);
csv.testMe(); // will print "in custom writeEnd"

如果我錯了,請糾正我,但這是一個最小的工作示例:

const { createWriteStream } = require('fs');

class CSV {
  constructor(path) {
    this.writeStream = createWriteStream(`${path}/csv/data.csv`);
    this.writeEnd = () => {};
  }
  write(data) {
    this.writeStream.write(`${data}\n`)
  }
  end() {
    this.writeStream.end()
    this.writeStream.on('finish', this.writeEnd)  
  }
};

const csv = new CSV(__dirname);
csv.writeEnd = () => console.log('Finished')
for (let i = 0; i < 1000000; i++) {
  csv.write('Hello World.');
}
csv.end()

我刪除了我認為不必要的可讀流,並且不應該調用destroy。

如果數據應在關閉之前刷新,請使用end()而不是destroy

https://nodejs.org/api/stream.html#stream_writable_destroy_error

您可以在最后看到“完成”。

無論如何,我不確定為什么要在這里使用類:

const fs = require('fs');

class CSV
{
    constructor(path) {
        this._ws = fs.createWriteStream(`${path}/csv/data.csv`);
    }

    write(data) {
        this._ws.write(`${data}\n`);
    }

    close() {
        const _this = this;
        return new Promise(function (resolve, reject) {
            _this._ws.once('finish', resolve);
            _this._ws.once('error', reject);
            _this._ws.end();
        });
    }
}

async function main()
{
    const csv = new CSV('path1');

    for (let i = 0; i < 1000000; ++i) {
        csv.write(`chunk ${i}`);
    }

    await csv.close();

    console.log('end');
}

function panic(error)
{
    console.error(error);
    process.exit(1);
}

// https://stackoverflow.com/a/46916601/1478566
main().catch(panic).finally(clearInterval.bind(null, setInterval(a=>a, 1E9)));

以下是沒有類的方法:

const fs = require('fs');

async function main()
{
    const ws = fs.createWriteStream('a.txt');

    for (let i = 0; i < 1000000; ++i) {
        ws.write(`chunk ${i}\n`);
    }

    ws.end();

    await promise_from_stream(ws);

    console.log('end');
}

function panic(error)
{
    console.error(error);
    process.exit(1);
}

function promise_from_stream(stream)
{
    /**
     * https://stackoverflow.com/a/34310963/1478566
     * > end and finish are the same event BUT on different types of
     * > Streams.
     * >   * stream.Readable fires ONLY end and NEVER finish
     * >   * stream.Writable fires ONLY finish and NEVER end
     */
    return new Promise(function (resolve, reject) {
        stream.once('end', resolve);
        stream.once('finish', resolve);
        stream.once('error', reject);
    });
}

// https://stackoverflow.com/a/46916601/1478566
main().catch(panic).finally(clearInterval.bind(null, setInterval(a=>a, 1E9)));

因此,正是通過不同答案和評論的共同努力,我才找到了一個簡單的解決方案! 非常感謝所有抽出寶貴時間分享建議的人。

我刪除了可讀流,因為它完全沒有必要,只對可寫流使用了write方法。 實例化后,我還將回調函數傳遞給構造函數。

這是我的最終代碼:

const { createWriteStream } = require('fs');

/**
 * @class CSV
 */
module.exports = class CSV {
  constructor(path, cb) {
    this.writeStream = createWriteStream(`${path}/csv/data.csv`);
    this.writeStream.on('finish', cb);
  }

  /**
   * @method write
   * @param {any} data
   */
  write(data) {
    this.writeStream.write(`${data}\n`);
  }

  /**
   * @method end
   */
  end() {
    this.writeStream.end();
  }
};

和測試文件:

const CSV = require('./shared/classes/csv');
const csv = new CSV(__dirname, cb);

function cb() {
  console.log('You win sir.');
}

for (let i = 0; i < 1000000; i++) {
  csv.write('Hello World.');
}

csv.end();

暫無
暫無

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

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