我试图使用nodejs readline
逐行读取文件,对于每一行,我想异步执行一些功能,然后继续到文件末尾。
const readline = require("readline");
const fs = require("fs");
let rl = readline.createInterface({
input: fs.createReadStream('b'),
crlfDelay: Infinity
});
rl.on('line', async (line) => {
console.log('start line');
await both();
console.log('end line');
});
rl.on('close', () => {
console.log('read complete');
});
function one() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('two'), 2000);
});
}
function two() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('two'), 3000);
});
}
async function both() {
ap = [];
ap.push(one());
ap.push(two());
console.log('processing line');
await Promise.all(ap);
console.log('line processed');
}
文件b
可以是带有某些行的任何文件,例如
1
2
3
4
5
6
7
我期望的输出如下:
开始行
线加工
行处理
终点线
。
。
。
但是,我无法维持订单。
据我了解,似乎'line'
事件正在发出,它一次又一次地调用回调!
有什么方法可以让此事件等待,直到手头的事件被异步处理(异步运行的各个步骤),然后重复执行。
**重要更新** 因此,用例文件将包含大约5GB以上的CSV文本。 而且我们的内存限制为<3GB,最大时间为15分钟(AWS Lambda)。
答案 0 :(得分:0)
据我了解,似乎“线路”事件正在发出 一次又一次地调用回调!。
是的,我认为这也是问题所在。
问题类似于Producer Consumer problem。
您可以做的是创建事件列表,并在每次调用时将line事件添加到事件列表中。
唯一的区别是Producer(正在创建的事件)永远不会填满缓冲区。但是需要提醒使用者(功能both
)消耗剩余的事件。如果没有事件,则消费者进入睡眠状态。每次发生新事件时,生产者都会检查消费者是否处于唤醒状态,如果不是,则会唤醒消费者。
您的解决方案应该是-
const readline = require("readline");
const fs = require("fs");
let rl = readline.createInterface({
input: fs.createReadStream('b'),
crlfDelay: Infinity
});
const lineEventsToProcess = [];
let bothRunning = false;
rl.on('line', (line) => {
// Add the line event to the list of line events
lineEventsToProcess.push(line)
// Both is not running i.e. the consumer is asleep
if (!bothRunning) {
both()
}
});
rl.on('close', () => {
console.log('read complete');
});
function one() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('two'), 2000);
});
}
function two() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('two'), 3000);
});
}
async function both() {
// Set bothRunning to true
bothRunning = true;
while(lineEventsToProcess.length > 0) {
console.log('start line');
ap = [];
ap.push(one());
ap.push(two());
console.log('processing line');
await Promise.all(ap);
console.log('line processed');
// Remove the first element
lineEventsToProcess.splice(0, 1)
console.log('end line');
}
// Both is not running anymore
bothRunning = false;
}
通过将行事件替换为setInterval对其进行了一些修改,以便可以对其进行测试。如果您想在浏览器中对其进行测试,或者有人遇到类似的问题-
const lineEventsToProcess = [];
let bothRunning = false;
setInterval(() => {
// Add the line event to the list of line events
lineEventsToProcess.push(1)
// Both is not running i.e. the consumer is asleep
if (!bothRunning) {
both();
}
}, 100);
function one() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('two'), 2000);
});
}
function two() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('two'), 3000);
});
}
async function both() {
// Set bothRunning to true
bothRunning = true;
while(lineEventsToProcess.length > 0) {
console.log('start line');
ap = [];
ap.push(one());
ap.push(two());
console.log('processing line');
await Promise.all(ap);
console.log('line processed');
// Remove the first element
lineEventsToProcess.splice(0, 1)
console.log('end line');
}
// Both is not running anymore
bothRunning = false;
}
如果需要更多说明,请在下面评论。
我并不是说这是最好的解决方案,但它应该可以工作。如果您想改善这一点,我建议通过为Producer和Consumers创建类来对代码进行模块化。在线上有大量针对Porducer-消费者问题的解决方案。