Node.js - превышение памяти при обработке массива через .map

Используемые модули:

const Bluebird = require('bluebird');
const fileSystem = Bluebird.promisifyAll(require('graceful-fs'));
const pathModule = require('path');
const xmlParser = Bluebird.promisifyAll(require('xml2js'));

Проблема

Нужно обработать много XML файлов. Создал функцию для получения списка всех путей:

function collectFilePaths(basePath) {
  return fileSystem.readdirAsync(basePath)
    .then(function(items) {
      return items.filter(function(item) {
        return fileSystem.statSync(pathModule.join(basePath, item)).isDirectory();
      });
    })
    .map(function(folder) {
      let folderPath = pathModule.join(basePath, folder);
      return fileSystem.readdirAsync(folderPath)
        .filter(function(fileName) {
          return pathModule.extname(fileName) === '.XML';
        })
        .map(function(fileName) {
          return pathModule.join(basePath, folder, fileName);
        });
    })
    .reduce(function(result, current) {
      return result.concat(current);
    });
}

Функции для чтения и парсинга:

function loadFileContent(filePath) {
  return fileSystem.readFileAsync('./' + filePath)
    .then(function(content) {
      return content;
    });
}

function convertXmlToObject(xmlData) {
  return xmlParser.parseStringAsync(xmlData)
    .then(function(result) {
      return result;
    });
}

Когда запускаю обработку всех файлов (более 20 тысяч), получаю ошибку нехватки памяти:

collectFilePaths('./XMLFiles')
  .map(function(filePath) {
    loadFileContent(filePath)
      .then(function(fileContent) {
        convertXmlToObject(fileContent)
          .then(function(parsedData) {
            console.log(parsedData);
          });
      });
  });

Ошибка: FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

Одиночный файл обрабатывается нормально. В чем может быть проблема?

У тебя проблема с concurrency - запускаешь все 20 тысяч файлов одновременно, поэтому память переполняется. Ограничь через {concurrency: 10} в .map(). Или используй .mapSeries() для последовательной обработки, либо батчами по 100-200 файлов. И в коде баг - в .map() не возвращаешь промис, добавь return перед loadFileContent(). Без этого цепочка промисов сломана.

Еще вариант - убери лишние промисы из цепочки, convertXmlToObject и loadFileContent просто возвращают то что получили. Попробуй увеличить heap через --max-old-space-size=8192 при запуске ноды. Какого размера твои XML файлы? Может стоит их отфильтровать по весу заранее.

Классика - грузишь все файлы в память разом. Переходи на streams или хотя бы чисти переменные после каждого файла. У меня с 15к файлов такое же было - спас process.nextTick() между обработками плюс gc() время от времени. Или раздели файлы на чанки по 50-100 штук, обрабатывай порциями с паузами.