[英]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.