javascript 在 for 循环中等待 promise

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48014050/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 07:44:38  来源:igfitidea点击:

Wait promise inside for loop

javascriptnode.jspromiseasync-awaitbluebird

提问by Jumpa

let currentProduct;

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

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

I'm using bluebird, the methods getAlland updatereturn promises. How can I say "Wait until the two promises return, then update the currentProduct value"? I'm quite new to JS...

我正在使用 bluebird,方法getAllupdate返回承诺。我怎么能说“等到两个承诺返回,然后更新 currentProduct 值”?我对JS很陌生......

回答by CRice

This will be straightforward if you can use async/await:

如果您可以使用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);
    */
}

Or if you can only use plain promises, you can loop through all your products, and put each promise in the .thenof the last loop. In that way, it will only advance to the next when the previous has resolved (even though it will have iterated the whole loop first):

或者,如果您只能使用普通的 Promise,则可以循环遍历所有产品,并将每个 Promise 放在.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))
}

In the second snippet, the loop just sets up the entire chain, but doesn't execute the code inside the .thenimmediately. Your getAllfunctions won't run until each prior one has resolved in turn (which is what you want).

在第二个片段中,循环只是设置了整个链,但不会.then立即执行内部的代码。您的getAll函数不会运行,直到每个先前的函数依次解决(这是您想要的)。

回答by Benjamin Gruenbaum

Here is how I'd do it:

这是我的做法:

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

No need to manually chain promises or iterate arrays by index :)

无需手动链接承诺或按索引迭代数组:)

回答by HMR

You may want to keep track of what products you've processed because when one fails you have no idea how many succeeded and you don't know what to correct (if roll back) or retry.

您可能想要跟踪您处理过的产品,因为当一个产品失败时,您不知道有多少成功,并且您不知道要纠正什么(如果回滚)或重试。

The async "loop" could be a recursive function:

异步“循环”可能是一个递归函数:

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);
}

Without async you can use recursion or reduce:

如果没有异步,您可以使用递归或减少:

//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

Here is how you'd call updateProducts:

以下是您调用 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
  )
)