Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/390.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Node.js-等待循环中抛出的所有承诺_Javascript_Node.js - Fatal编程技术网

Javascript Node.js-等待循环中抛出的所有承诺

Javascript Node.js-等待循环中抛出的所有承诺,javascript,node.js,Javascript,Node.js,我正在处理Node.js中的一个循环,它为循环的每个迭代执行两个任务。为了简化,代码总结如下: 从网页提取产品元数据(阻止任务) 将所有产品元数据保存到数据库(异步任务) save操作(2)将在数据库中执行大约800个操作,并且不需要阻止主线程(我仍然可以从网页中提取产品元数据) 因此,尽管如此,等待产品被保存是没有任何意义的。但是,如果我在不等待承诺的情况下抛出承诺,那么在循环的最后一次迭代中,Node.js进程将退出,所有挂起的操作都不会完成 解决这个问题的最佳方法是什么?有没有可能在没有完

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

  • 从网页提取产品元数据(阻止任务)
  • 将所有产品元数据保存到数据库(异步任务)
  • save操作(
    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