Правильно пакетные вложенные обещания в узле - PullRequest
0 голосов
/ 30 января 2020

Я запускаю knex seed в узле и мне нужно выполнить дополнительный запрос к моей базе данных из-за ограничений на моем сервере. Я начинаю привыкать к обещаниям и асинхронности / ожиданию, но мне трудно заставить его работать на нескольких уровнях (что особенно меня отталкивает в этот момент, так это то, что оно, похоже, мешает пакетированию в так, что я не совсем понимаю). Мой seed файл выглядит так:

exports.seed = async function(knex) {
  const fs = require('fs');
  const _ = require('lodash');

  function get_event_id(location) {
    return knex('events')
      .where({location: location})
      .first()
      .then(result => { return result['id']; })
      .finally(() => { knex.destroy() })
  }

  function createImage(row, event_id) {
    return {
      name: row[4],
      event_id: event_id
    }
  };

  async function run_query(line) {
      let row = line.split(',');
      let event_id = await get_event_id(row[0]);
      return createImage(row, event_id);
  };

  async function run_batch(batch) {

      return Promise.all(batch.map(run_query));
  } 

  const file = fs.readFileSync('./data.csv');
  const lines = file.toString().replace(/[\r]/g, '').split('\n').slice(1,60); // skip csv header, then run first 59 lines

  const batches = _.chunk(lines, 30); // set batch size

  let images = await Promise.all(batches.map(run_batch));

  console.log(_.flatten(images).length);

};

Моя база данных может обрабатывать 30 запросов одновременно. Все разрешается правильно, если я запускаю один пакет, используя .slice(1,30) в строке, где определено lines. Но работа с 60, как указано выше, дает мне ER_TOO_MANY_USER_CONNECTIONS: User already has more than 'max_user_connections' active connections.

Сценарий завершается, если я изменяю содержимое run_batch на return batch.map(run_query), и возвращает правильное количество записей (поэтому, похоже, что пакетирование выполняется правильно). Но тогда Обещания еще ожидаются. Чего мне не хватает, и есть ли более элегантный способ сделать это?

1 Ответ

3 голосов
/ 30 января 2020

В этой строке:

let images = await Promise.all(batches.map(run_batch));

Вы пытаетесь запустить ВСЕ партии параллельно, что полностью побеждает вашу порцию.

Вы можете использовать обычный for l oop с await вместо .map(), поэтому вы выполняете партию, дождитесь, пока она завершится sh, а затем запустите следующую партию.

let allResults = [];
for (let batch of batches) {
     let images = await run_batch(batch);
     allResults.push(...images);
}
console.log(allResults);

К вашему сведению, вы можете воспользоваться количество функций, написанных людьми для обработки большого массива, в котором одновременно выполняется не более N запросов. Для этого не требуется разбивать данные вручную на пакеты. Вместо этого они отслеживают количество запросов, поступающих одновременно, и запускают желаемое количество запросов, а по окончании запускают другой, собирая результаты для вас.

runN(fn, limit, cnt, options): L oop через API на несколько запросов

pMap(array, fn, limit): Сделайте несколько запросов к API, который может обрабатывать только 20 одновременно

rateLimitMap(array, requestsPerSec, maxInFlight, fn): Правильный метод asyn c для максимального числа запросов в секунду

mapConcurrent(array, maxConcurrent, fn): Promise.all () потребляет весь мой оперативный памяти

Есть также функции для этого, встроенные в библиотеку обещаний Bluebird и библиотеку Asyn c - обещания .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...