我正在处理Node.js中的循环,该循环为循环的每次迭代执行两项任务。为简化起见,代码摘要如下:
保存操作(2
)将在数据库中执行大约800次操作,并且不需要阻塞主线程(我仍然可以从网页中提取产品元数据)。
因此,话虽如此,等待产品保存没有任何意义。但是,如果我不等待就抛出承诺,则在循环的最后一次迭代中,Node.js进程将退出,并且所有未完成的操作都不会完成。
哪个是解决此问题的最佳方法?是否有可能在没有完成承诺或发射者计数器的情况下实现它?谢谢。
for (let shop of shops) {
// 1
const products = await extractProductsMetadata(shop);
// 2
await saveProductsMetadata(products);
}
答案 0 :(得分:2)
将诺言收集到一个数组中,然后在其上使用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将自动退出。这意味着在完成上面的代码之后,您应该关闭所有数据库连接/服务器/等等。
答案 1 :(得分:1)
我们正在创建要处理的数据包。当我们处理数据时,我们同步进行所有获取,并异步进行所有保存。
我还没有处理故障部分,我让您将其添加到其中。适当的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);