web-dev-qa-db-ja.com

forループ内でプロミスを待つ

let currentProduct;

for (let i = 0; i < products.length; i++) { 
    currentProduct = products[i];

    subscription.getAll(products[i]._id)
        .then((subs) => {
            update(subs, currentProduct);
        });
}

私はbluebirdを使用しています。メソッドgetAllおよびpdateはpromiseを返します。 「2つのプロミスが戻るまで待ってから、currentProduct値を更新する」と言うにはどうすればよいですか?私はJSが初めてです...

7
Jumpa

async/awaitを使用できる場合、これは簡単です。

// Make sure that this code is inside a function declared using
// the `async` keyword.
let currentProduct;

for (let i = 0; i < products.length; i++) { 
    currentProduct = products[i];

    // By using await, the code will halt here until
    // the promise resolves, then it will go to the
    // next iteration...
    await subscription.getAll(products[i]._id)
        .then((subs) => {
            // Make sure to return your promise here...
            return update(subs, currentProduct);
        });

    // You could also avoid the .then by using two awaits:
    /*
    const subs = await subscription.getAll(products[i]._id);
    await update(subs, currentProduct);
    */
}

または、単純なプロミスのみを使用できる場合は、すべての製品をループし、各プロミスを最後のループの.thenに入れることができます。この方法では、前のループが解決された場合にのみ、次のループに進みます(最初にループ全体を反復した場合でも)。

let currentProduct;

let promiseChain = Promise.resolve();
for (let i = 0; i < products.length; i++) { 
    currentProduct = products[i];

    // Note that there is a scoping issue here, since
    // none of the .then code runs till the loop completes,
    // you need to pass the current value of `currentProduct`
    // into the chain manually, to avoid having its value
    // changed before the .then code accesses it.

    const makeNextPromise = (currentProduct) => () => {
        // Make sure to return your promise here.
        return subscription.getAll(products[i]._id)
            .then((subs) => {
                // Make sure to return your promise here.
                return update(subs, currentProduct);
            });
    }

    // Note that we pass the value of `currentProduct` into the
    // function to avoid it changing as the loop iterates.
    promiseChain = promiseChain.then(makeNextPromise(currentProduct))
}

2番目のスニペットでは、ループはチェーン全体を設定するだけですが、.then内のコードをすぐには実行しません。 getAll関数は、前の各関数が順番に解決されるまで実行されません(これが目的です)。

13
CRice

ここに私がそれをする方法があります:

for (let product of products) { 
  let subs = await subscription.getAll(product._id);
  await update(subs, product);
}

手動でプロミスをチェーンしたり、インデックスで配列を繰り返す必要はありません:)

7

失敗した場合、成功した製品の数が分からず、修正(ロールバックの場合)または再試行がわからないため、処理した製品を追跡する必要があります。

非同期の「ループ」は、再帰関数である可能性があります。

const updateProducts = /* add async */async (products,processed=[]) => {
  try{
    if(products.length===0){
      return processed;
    }
    const subs = await subscription.getAll(products[0]._id)
    await update(subs, product);
    processed.Push(product[0]._id);  
  }catch(err){
    throw [err,processed];
  }
  return await updateProducts(products.slice(1),processed);
}

非同期なしで、再帰を使用するか減らすことができます:

//using reduce
const updateProducts = (products) => {
  //keep track of processed id's
  const processed = [];
  return products.reduce(
    (acc,product)=>
      acc
      .then(_=>subscription.getAll(product._id))
      .then(subs=>update(subs, product))
      //add product id to processed product ids
      .then(_=>processed.Push(product._id)),
    Promise.resolve()
  )
  //resolve with processed product id's
  .then(_=>processed)
  //when rejecting include the processed items
  .catch(err=>Promise.reject([err,processed]));
}

//using recursion
const updateProducts = (products,processed=[]) =>
  (products.length!==0)
    ? subscription.getAll(products[0]._id)
      .then(subs=>update(subs, product))
      //add product id to processed
      .then(_=>processed.Push(products[0]._id))
      //reject with error and id's of processed products
      .catch(err=>Promise.reject([err,processed]))
      .then(_=>updateProducts(products.slice(1),processed))
    : processed//resolve with array of processed product ids

UpdateProductsを呼び出す方法は次のとおりです。

updateProducts(products)
.then(processed=>console.log("Following products are updated.",processed))
.catch(([err,processed])=>
  console.error(
    "something went wrong:",err,
    "following were processed until something went wrong:",
    processed
  )
)
2
HMR