Javascript Node.js-等待循环中抛出的所有承诺
我正在处理Node.js中的一个循环,它为循环的每个迭代执行两个任务。为了简化,代码总结如下:Javascript Node.js-等待循环中抛出的所有承诺,javascript,node.js,Javascript,Node.js,我正在处理Node.js中的一个循环,它为循环的每个迭代执行两个任务。为了简化,代码总结如下: 从网页提取产品元数据(阻止任务) 将所有产品元数据保存到数据库(异步任务) save操作(2)将在数据库中执行大约800个操作,并且不需要阻止主线程(我仍然可以从网页中提取产品元数据) 因此,尽管如此,等待产品被保存是没有任何意义的。但是,如果我在不等待承诺的情况下抛出承诺,那么在循环的最后一次迭代中,Node.js进程将退出,所有挂起的操作都不会完成 解决这个问题的最佳方法是什么?有没有可能在没有完
2
)将在数据库中执行大约800个操作,并且不需要阻止主线程(我仍然可以从网页中提取产品元数据)
因此,尽管如此,等待产品被保存是没有任何意义的。但是,如果我在不等待承诺的情况下抛出承诺,那么在循环的最后一次迭代中,Node.js进程将退出,所有挂起的操作都不会完成
解决这个问题的最佳方法是什么?有没有可能在没有完成承诺或排放者计数器的情况下实现这一目标?谢谢
for (let shop of shops) {
// 1
const products = await extractProductsMetadata(shop);
// 2
await saveProductsMetadata(products);
}
收集数组中的承诺,然后使用
Promise.all
:
const storePromises = [];
for (let shop of shops) {
const products = await extractProductsMetadata(shop); //(1)
storePromises.push(saveProductsMetadata(products)); //(2)
}
await Promise.all(storePromises);
// ... all done (3)
通过这个过程,(1)将一个接一个地运行,(2)将并行运行,(3)将随后运行
当然,您也可以并行运行(1)和(2):
await Promise.all(shops.map(async shop => {
const products = await extractProductsMetadata(shop); //(1)
await saveProductsMetadata(products);
}));
如果其中一个承诺出现错误,您可以使用try/catch
块来处理,以确保所有其他商店不会受到影响:
await Promise.all(shops.map(async shop => {
try {
const products = await extractProductsMetadata(shop); //(1)
await saveProductsMetadata(products);
} catch(error) {
// handle it here
}
}));
如何通知节点完成该过程
您可以手动调用
process.exit(0)代码>,但这隐藏了真正的问题:如果不再连接侦听器,NodeJS将自动退出。这意味着您应该在完成上述代码后关闭所有数据库连接/服务器等。我们正在创建要处理的数据包。当我们处理数据时,我们同步执行所有get,异步执行所有保存
我没有处理失败的部分,我让你把它加进去。适当的try/catch
或函数封装即可
/**
* Call the given functions that returns promises in a queue
* options = context/args
*/
function promiseQueue(promisesFuncs, options = {}, _i = 0, _ret = []) {
return new Promise((resolve, reject) => {
if (_i >= promisesFuncs.length) {
return resolve(_ret);
}
// Call one
(promisesFuncs[_i]).apply(options.context || this, options.args || [])
.then((ret: any) => promiseQueue(promisesFuncs, _i + 1, options, [
..._ret,
ret,
]))
.then(resolve)
.catch(reject);
});
}
function async executePromiseAsPacks(arr, packSize, _i = 0) {
const toExecute = arr.slice(_i * packSize, packSize);
// Leave if we did execute all packs
if (toExecute.length === 0) return true;
// First we get all the data synchronously
const products = await promiseQueue(toExecute.map(x => () => extractProductsMetadata(x)));
// Then save the products asynchronously
// We do not put await here so it's truly asynchronous
Promise.all(toExecute.map((x, xi) => saveProductsMetadata(products[xi])));
// Call next
return executePromiseAsPacks(arr, packSize, _i + 1);
}
// Makes pack of data to treat (we extract synchronously and save asynchronously)
// Made to handle huge dataset
await executePromisesAsPacks(shops, 50);
您可以这样做:1)创建一个承诺数组,其中存储与产品相关的所有承诺,并在for循环后执行它们,只有在所有承诺完成后才退出流程(使用Promise.all)2)重新设计流程,以便另一个节点流程/脚本负责执行所有查询。保存操作(2)将在一个数据库中执行大约800个操作,并且不需要阻止主线程(我仍然可以从网页中提取产品元数据)。
可以一次执行多个保存吗?是@JaromandaX,可以一次执行多个保存!但是extractProductsMetadata
必须一次运行一个,对吗?是的,这也是正确的@jaromandax在一次承诺失败的情况下仍然会停止。每个承诺可能有数百家商店和数千种产品。我不认为同时等待10万个承诺是个好主意。。。尤其是在处理大量的数据库操作时。@grégory我认为通过“抛出承诺”,OP谈论的是一种“开火并忘记”的策略,而不是实际上在承诺内抛出。@elias是的,这实际上应该是一个数据库聚合,数据库可以在自己的WO上运行,而无需NodeJS。@EliasGarcia-错误处理和并发性是不同的thing@JonasWilms完全同意,现在正是编辑它的时候;)这段代码将同时“提取然后保存”每个x
——我认为OP不希望多个提取同时运行(这段代码可以做到)——请参阅验证这一点的相关注释fact@Eliasgarcia我已经编辑了我的代码,请询问是否有不清楚的地方我正在查看你的代码,我曾想过这样做,但我也认为这将是一种“更本土化”的方式,但我认为这是不可能的。我想我们走这条路吧,非常感谢你的例子!:)如果将任务本身移出executepromissesaspack