簡體   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