繁体   English   中英

Node.js - 等待循环中抛出的所有promise

[英]Node.js - Await for all the promises thrown inside a loop

我正在处理Node.js中的循环,它为循环的每次迭代执行两个任务。 为简化起见,代码总结如下:

  1. 从网页中提取产品元数据(阻止任务)。
  2. 将所有产品元数据保存到数据库(异步任务)。

保存操作( 2 )将在数据库中执行大约800个操作,并且不需要阻止主线程(我仍然可以从网页中提取产品元数据)。

所以,正如所说,等待产品被保存没有任何意义。 但是如果我在没有等待它们的情况下抛出promise,则在循环的最后一次迭代中,Node.js进程退出并且所有未决操作都没有完成。

哪种解决方法最好? 是否有可能在没有完成承诺或发射器的计数器的情况下实现它? 谢谢。

for (let shop of shops) {
  // 1
  const products = await extractProductsMetadata(shop);

  // 2
  await saveProductsMetadata(products);
}

收集数组中的promises,然后在其上使用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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM