在 Node 中正确批处理嵌套的承诺

use*_*072 1 javascript asynchronous node.js promise knex.js

knex seed在 Node 中运行 a并且由于我的服务器的限制需要对我的数据库进行批处理。我开始掌握 Promise 和 async/await 的窍门,但是我无法让它在多个级别深度工作(此时特别让我失望的是它似乎干扰了批处理我无法理解的方式)。我的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);

};
Run Code Online (Sandbox Code Playgroud)

我的数据库一次可以处理 30 个查询。如果我.slice(1,30)lines定义的行上运行单个批处理,一切都会正确解决。但是像上面那样用 60 运行给了我ER_TOO_MANY_USER_CONNECTIONS: User already has more than 'max_user_connections' active connections.

如果我更改run_batchto的内容,脚本将完成return batch.map(run_query),并返回正确的条目数(因此它似乎正在正确批处理)。但随后的承诺仍然悬而未决。我错过了什么,有没有更优雅的方法来做到这一点?

jfr*_*d00 5

在这一行:

let images = await Promise.all(batches.map(run_batch));
Run Code Online (Sandbox Code Playgroud)

您正在尝试并行运行所有批次,这完全破坏了您的分块。

您可以使用常规for循环await而不是.map()运行批处理,等待它完成,然后运行下一个批处理。

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

仅供参考,您可能会受益于人们为处理同时进行的不超过 N 个请求的大型数组而编写的任意数量的函数。这些不需要您手动将数据分成批次。相反,它们会同时监控有多少请求在进行中,并启动您想要的请求数量,当一个请求完成时,它们会启动另一个请求,为您收集结果。

runN(fn, limit, cnt, options)在多个请求上循环访问 API

pMap(array, fn, limit):向一次只能处理 20 个的 api 发出多个请求

rateLimitMap(array, requestsPerSec, maxInFlight, fn)每秒最大请求的正确异步方法

mapConcurrent(array, maxConcurrent, fn): Promise.all() 消耗了我所有的 ram

Bluebird promise 库Async-promises 库中还内置了一些功能可以做到这一点。