如何在事件上的NodeJ中使用readline(等待流关闭)

时间:2019-06-15 18:50:11

标签: node.js async-await readline

我正在创建一个机器人,当它收到消息时开始读取文本文件,并使用文件内容对消息进行响应。

不幸的是,我无法摆脱这个异步地狱,我只会得到错误,未定义或承诺

最后一个实验是这样:

const fs = require('fs');
const readline = require('readline');

// bot.listen("message").reply(responseText())

function readFile(file) {
    var text = '';
        var readInterface = readline.createInterface({
            input: fs.createReadStream(file),
            terminal: false
        });

        readInterface.on('line', function(line) {
            linea = line.trim();
            console.log(linea);
            text += linea;
        }).on('close', function() {
            return text;
        });
    });
}

async function responseText() {
    var content = await readFile("file.txt");
    content.then(function(data) {
        return data;
    })
}

我想得到的是延迟响应,直到获得文件内容为止。 我知道该节点基于异步,但是我不知道该如何处理!

谢谢

3 个答案:

答案 0 :(得分:0)

如果您想使用async-await,则需要创建一个诺言并将其返回。

function readFile(file) {
    return new Promise((res, rej) => {
        try {
            var text = '';
            var readInterface = readline.createInterface({
                input: fs.createReadStream(file),
                terminal: false
            });

            readInterface
                .on('line', function (line) {
                    linea = line.trim();
                    text += linea;
                })
                .on('close', function () {
                    res(text);
                });
        } catch(err){
            rej(err)
        }
    });
}

答案 1 :(得分:0)

如果您使用express.js或基于它的任何框架,则可以将readstream管道传递给响应,因为express的响应是以以下内容开头的流:

const es = require('event-stream')
...
let getFileStream = path => (
fs.createReadStream(path)
.pipe(es.split())
.pipe(es.map(function (data, cb) { 
      cb(null
        , inspect(JSON.parse(data)))  
    }))
);

router.get('/message', function (req, res, next) {
   let file$ = getFileStream(yourFilePath)
       file$.on('error', next).pipe(res)

})

如果需要转换文件内容,可以使用transform stream或如上例所示的同步事件流映射。想法是始终在流级别上处理文件内容,以避免必须将整个文件内容加载到内存中。

您实际上并不想将整个文件内容缓冲在内存中。在忙碌的一天中,使用大型文件可能会很快成为一个问题。您需要的是将文件流直接传输到浏览器。相同的原则适用于任何类型的消费者。

当然,如果该机制全部是内部机制,则只应沿文件路径传递或传递实际流,直到需要实际打开文件并对内容执行某些操作为止。在这种情况下,您将返回流工具箱,无论它是本机的node.js流API实现,事件流包还是某种可观察的库,例如rxjs。

答案 2 :(得分:0)

我在一个应用程序中遇到了类似的问题,该应用程序监视目录中的新文件,读取文件,并根据文件内容返回派生数据。我的阅读器功能基于来自nodejs文档的async example。仅在完全读取文件之后,才返回包含上下文的options

const { createReadStream } = require('fs')
const { createInterface } = require('readline')
const { once } = require('events')

// Reader.js
async function Reader (options) {
  let { file, cb } = options
  let fileStream = createReadStream(file)

  const readInterface = createInterface({
    input: fileStream,
    crlfDelay: Infinity
  })

  readInterface.on('line', (line) => {
    cb(line)
  })

  await once(readInterface, 'close')
  return options
}
module.exports = Reader

然后我有了一个导入我的Reader并定义如何使用它的文件。我定义了一个回调函数来传递给line事件监听器。我将回调绑定到传递给Reader函数的options对象。在里面  readFile函数我确保return到Reader的调用,这是一个承诺。

/**
 * @desc callback to instruct what to do with each line read
 *
 * @param {*} line
 */
const readFileLine = function (line) {
  linea = line.trim();
  console.log(linea);
  text += linea;
  this.context += linea
}

/**
 * @desc once the added file is ready to be processed read file line by line
 * @listens {Event} listens for `process` event
*/
const readFile = (options) => {
  return Reader(options)
}

/**
 * @desc Call the file reader and do what you need with the reponse
 * 
*/
const getResponseFromFiles = (file) => {
  const opts = {}
  opts.cb = readFileLine.bind(opts)
  opts.context = ''
  opts.file = file
  readFile(opts)
  .then(data => {
      process.exitCode = 0
      console.log(data)
      return data
    })
    .catch(err => {
      process.exitCode = 1
      console.log(err.message)
    })
}