【问题标题】:How can I make a readline await async promise?如何使 readline 等待异步承诺?
【发布时间】:2021-11-02 23:08:36
【问题描述】:

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

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 仅在异步函数和模块的顶层主体中有效

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

【问题讨论】:

  • Node 从 14.3 版本开始支持顶级等待(无需封装在 async 函数中)。 pprathameshmore.medium.com/…
  • @JeremyThille true,但是此时您必须将脚本/项目定义为type:module,这对于现有项目意味着相当多的重构(当然,如果这是一个新脚本,那么很容易做到)。

标签: javascript node.js async-await promise readline


【解决方案1】:

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

你的问题中的错误:

顶级等待(await 未包装在 async 函数中)仅在“作为 ES 模块”运行 Node.js 时才有可能,即当您使用 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 对等待事件/回调没有任何作用。

您可以通过等待 Readline 的“关闭”事件来解决这个问题,然后才解决承诺。

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 () => { ... })();

【讨论】:

    【解决方案2】:

    将函数调用包装成异步IIFE

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

    【讨论】:

    • 在函数开头添加分号 ;(async()=>{
    【解决方案3】:

    试试这个:

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

    【讨论】:

      猜你喜欢
      • 2017-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-20
      • 2018-02-03
      • 2018-03-05
      • 1970-01-01
      相关资源
      最近更新 更多