如何让 readline 等待异步承诺?

How can I make a readline await async promise?

在 NodeJS 上,我需要为日志研究目的创建一个类似 grep 的函数,并且我正在尝试使用 readline 来实现它(因为我不想要 readline-sync)。我已经阅读了很多消息、教程、文档、Whosebug 帖子等等,但我不明白如何让它发挥作用。

const grep = async function(pattern, filepath){
    return new Promise((resolve, reject)=>{
        let regex = new RegExp(pattern);
        let fresult = ``;

        let lineReader = require(`readline`).createInterface({
            input: require(`fs`).createReadStream(filepath)
        });

        lineReader.on(`line`, function (line) {
            if(line.match(regex)){
                fresult += line;
            }
        });

        resolve(fresult);
    });
}

let getLogs = await grep(/myregex/gi, filepath);
console.log(getLogs);

这给了我:

SyntaxError: await is only valid in async functions and the top level bodies of modules

我哪里错了?我觉得我很好,但犯了一个初学者的错误,在我眼皮底下跳舞。

将函数调用包装成异步 IIFE:

(async()=>{
  let getLogs = await grep(/myregex/gi, filepath);
  console.log(getLogs);
})()

试试这个:

async function run(){

let getLogs = await grep(/myregex/gi, `/`);
console.log(getLogs);
}

run();

其他答案遗漏的是您的代码中有两个问题。

你问题中的错误:

只有当 运行 Node.js “作为 ES 模块”时,才可能出现顶级等待(await 未包含在 async 函数中),也就是当您使用 import {xyz} from 'module' 而不是 const {xyz} = require('module').

如前所述,解决此问题的一种方法是将其包装在异步函数中:

// Note: You omitted the ; on line 18 (the closing } bracket)
// which will cause an error, so add it.

(async () => {
let getLogs = await grep(/myregex/gi, filepath);
console.log(getLogs);
})();

另一种选择是将文件另存为 .mjs,而不是 .js。这意味着您必须使用 import 而不是 require.

第三个选项是创建一个package.json并指定"type": "module"。同样的规则适用。

一个更根本的问题:

当您调用 resolve() 时,lineReader.on('line') 中的事件处理程序尚未执行。这意味着您将解析为一个空字符串,而不是用户的输入。毕竟,声明一个函数 async 不会等待 events/callbacks。

您可以通过等待 Readline 的 'close' 事件来解决此问题,然后再解决承诺。

const grep = async function(pattern, filepath){
    return new Promise((resolve, reject)=>{
        let regex = new RegExp(pattern);
        let fresult = ``;

        let lineReader = require(`readline`).createInterface({
            input: require(`fs`).createReadStream(filepath)
        });

        lineReader.on(`line`, function (line) {
            if(line.match(regex)){
                fresult += line;
            }
        });

        // Wait for close/error event and resolve/reject
        lineReader.on('close', () => resolve(fresult));
        lineReader.on('error', reject);
    });
}; // ";" was added

(async () => {
  let getLogs = await grep(/myregex/gi, 'foo');
  console.log("output", getLogs);
})();

小费

events 模块有一个名为 once 的方便实用函数,可以让您以更短更清晰的方式编写代码:

const { once } = require('events');
// If you're using ES Modules use: 
// import { once } from 'events'

const grep = async function(pattern, filepath){
    // Since the function is 'async', no need for 
    // 'new Promise()'

    let regex = new RegExp(pattern);
    let fresult = ``;

    let lineReader = require(`readline`).createInterface({
        input: require(`fs`).createReadStream(filepath)
    });

    lineReader.on(`line`, function (line) {
        if(line.match(regex)){
            fresult += line;
        }
    });

    // Wait for the first 'close' event
    // If 'lineReader' emits an 'error' event, this
    // will throw an exception with the error in it.
    await once(lineReader, 'close');
    return fresult;
};

(async () => {
  let getLogs = await grep(/myregex/gi, 'foo');
  console.log("output", getLogs);
})();

// If you're using ES Modules: 
// leave out the (async () => { ... })();