对事件使用异步等待

时间:2020-04-23 10:14:56

标签: javascript node.js async-await dom-events readline

我试图使用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)。

1 个答案:

答案 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-消费者问题的解决方案。