在 Node 中正确地批量嵌套承诺
Properly batch nested promises in Node
我在 Node 中 运行ning knex seed
,由于服务器的限制,我需要对我的数据库进行批量查询。我开始掌握 promises 和 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);
};
我的数据库一次可以处理 30 个查询。如果我在定义 lines
的行上使用 .slice(1,30)
运行 单个批次,一切都会正确解决。但是 运行 上面的 60 给了我 ER_TOO_MANY_USER_CONNECTIONS: User already has more than 'max_user_connections' active connections
。
如果我将 run_batch
的内容更改为 return batch.map(run_query)
,并且它 returns 的条目数正确(因此它似乎正确地进行了批处理),脚本就会完成。但是 Promise 仍然悬而未决。我错过了什么,有没有更优雅的方法来做到这一点?
这一行:
let images = await Promise.all(batches.map(run_batch));
您正在尝试 运行 并行处理所有批次,这完全破坏了您的分块。
您可以使用常规 for
循环 await
而不是 .map()
这样您 运行va 批处理,等待它完成,然后 运行下一批。
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)
:
pMap(array, fn, limit)
:
rateLimitMap(array, requestsPerSec, maxInFlight, fn)
: Proper async method for max requests per second
mapConcurrent(array, maxConcurrent, fn)
:
中也内置了执行此操作的功能
我在 Node 中 运行ning knex seed
,由于服务器的限制,我需要对我的数据库进行批量查询。我开始掌握 promises 和 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);
};
我的数据库一次可以处理 30 个查询。如果我在定义 lines
的行上使用 .slice(1,30)
运行 单个批次,一切都会正确解决。但是 运行 上面的 60 给了我 ER_TOO_MANY_USER_CONNECTIONS: User already has more than 'max_user_connections' active connections
。
如果我将 run_batch
的内容更改为 return batch.map(run_query)
,并且它 returns 的条目数正确(因此它似乎正确地进行了批处理),脚本就会完成。但是 Promise 仍然悬而未决。我错过了什么,有没有更优雅的方法来做到这一点?
这一行:
let images = await Promise.all(batches.map(run_batch));
您正在尝试 运行 并行处理所有批次,这完全破坏了您的分块。
您可以使用常规 for
循环 await
而不是 .map()
这样您 运行va 批处理,等待它完成,然后 运行下一批。
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)
:
pMap(array, fn, limit)
:
rateLimitMap(array, requestsPerSec, maxInFlight, fn)
: Proper async method for max requests per second
mapConcurrent(array, maxConcurrent, fn)
: