узел, вложенный в readline и причины записи MaxListenersExceededWarning - PullRequest
0 голосов
/ 26 марта 2020

Я хочу написать скрипт, который будет принимать записи строк из файла source и искать эту строку в каждой строке файла search. Если есть совпадение, я хочу, чтобы строка поиска была скопирована в файл output.

С моим текущим сценарием я получаю только одну строку, записанную в выходной файл, и кучу ошибок MaxListenersExceededWarning. Но как может быть так много ошибок, когда у меня только 3 потока?

Любая помощь приветствуется! :)

Ошибки:

(node:34238) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 end listeners added to [Readable]. Use emitter.setMaxListeners() to increase limit
(node:34238) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 finish listeners added to [Readable]. Use emitter.setMaxListeners() to increase limit
(node:34238) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [Readable]. Use emitter.setMaxListeners() to increase limit
(node:34238) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 close listeners added to [Readable]. Use emitter.setMaxListeners() to increase limit
(node:34238) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 readable listeners added to [Readable]. Use emitter.setMaxListeners() to increase limit

Вот мой сценарий:

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

const sourceEngramFile = './super-structures-engrams.txt'
const searchEngramConfig = './all-engram-overrides.ini'
const outputEngramConfig = './output-super-structures.ini'

async function writeLine(stream, line) {
  return new Promise(resolve => {
    stream.write(line, 'utf8', resolve)
  })
}

async function processLineByLine() {
  const sourceStream = fs.createReadStream(sourceEngramFile)
  const searchStream = fs.createReadStream(searchEngramConfig)
  const outputStream = fs.createWriteStream(outputEngramConfig)

  const sourceRL = readline.createInterface({
    input: sourceStream,
    crlfDelay: Infinity,
  })

  const searchRL = readline.createInterface({
    input: searchStream,
    crlfDelay: Infinity,
  })

  // Possibly the error is caused by these nested loops/awaits
  for await (const sourceLine of sourceRL) {
    for await (const searchLine of searchRL) {
      if (searchLine.includes(`EngramEntry_${sourceLine}`)) {
        await writeLine(outputStream, searchLine)
      }
    }
  }
}

processLineByLine()

Исходный файл выглядит следующим образом:

Wall_Wood
Wall_Tek
Wall_Greenhouse
...

поиск файла выглядит так:

OverrideNamedEngramEntries=(EngramClassName="EngramEntry_Wall_Wood_C",EngramLevelRequirement=11,EngramPointsCost=7,EngramHidden=false,RemoveEngramPreReq=true)
OverrideNamedEngramEntries=(EngramClassName="EngramEntry_Hatchframe_Adobe_C",EngramLevelRequirement=16,EngramPointsCost=8,EngramHidden=false,RemoveEngramPreReq=true)
OverrideNamedEngramEntries=(EngramClassName="EngramEntry_Doorframe_Wood_C",EngramLevelRequirement=11,EngramPointsCost=6,EngramHidden=false,RemoveEngramPreReq=true)
...

1 Ответ

1 голос
/ 26 марта 2020

Проблема с maxListeners вызвана попыткой многократно использовать один и тот же объект searchRL для внутреннего for l oop. Каждый раз, когда вы пытаетесь использовать его, он добавляет новый набор слушателей в базовый readStream, и в итоге вы превышаете количество слушателей, для которых выдается предупреждение. Я смог воспроизвести это предупреждение в простом тестовом приложении.

Это также тот случай, когда ваш внутренний for l oop работает только в первый раз, хотя внешний l oop все равно работает. После этого searchStream полностью расходуется и больше не генерирует выходной сигнал.

Ваш l oop здесь:

  // Possibly the error is caused by these nested loops/awaits
  for await (const sourceLine of sourceRL) {
    for await (const searchLine of searchRL) {
      if (searchLine.includes(`EngramEntry_${sourceLine}`)) {
        await writeLine(outputStream, searchLine)
      }
    }
  }

пытается повторять поток searchRL снова и снова. Это не работает, потому что основной поток чтения полностью расходуется на первой итерации, и с тех пор итератору нечего читать. Эта же проблема также вызывает проблему с EventListener.

Если searchStream может разумно поместиться в памяти, то вам, вероятно, следует прочитать его в памяти один раз, чтобы использовать его снова и снова в итерации sourceStream. Если нет, и вы хотите итерировать его из файла каждый раз, то вам, вероятно, придется каждый раз создавать новый поток и новый объект readLine через внешний l oop, чтобы у вас был поток fre sh для итерации.

Чтобы продемонстрировать проблему, о которой я говорю, вот небольшая программа, которая ничего не делает, кроме попытки повторять один и тот же объект readLine дважды. Первая итерация получает данные, которые мы хотим. Второй не получает данных, потому что базовый поток чтения полностью использовался первой итерацией (например, указатель файла в потоке указывает на конец файла и, следовательно, ему больше нечего читать).

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

async function run() {
    let rl = readline.createInterface({input: fs.createReadStream("file1.txt")});

    rl.on('error', err => {
        console.log(err);
    });

    console.log("start1");
    for await (const line of rl)  {
        console.log(line);
    }
    console.log("start2");
    for await (const line of rl)  {
        console.log(line);
    }
}

run().then(() => {
    console.log("done");
}).catch(err => {
    console.log(err);
});

К сожалению, readStreams, которые указывают на файл, не имеют встроенного способа сбрасывать свое состояние обратно в начало файла, поэтому я думаю, что вам нужно просто создать новый поток и новый объект readLine или прочитать поток в память один раз и использовать его из памяти, а не из файла каждый раз через l oop.

...