узел, вложенный readline и причины записи MaxListenersExceededWarning

Я хочу написать скрипт, который берет строковые записи из 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 ответ


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

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

Ваш цикл здесь:

  // 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 поток searchRL снова и снова. Это не работает, потому что основной поток чтения полностью расходуется на первой итерации, и с тех пор итератору нечего читать. Эта же проблема также вызывает проблему EventListener.

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

Чтобы продемонстрировать проблему, о которой я говорю, вот небольшая программа, которая ничего не делает, кроме как пытается дважды повторить один и тот же объект 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 или прочитать поток в память один раз и использовать его из памяти, а не из файла каждый раз через цикл.


Есть идеи?

10000