在 for 循环中等待 promise
Posted
技术标签:
【中文标题】在 for 循环中等待 promise【英文标题】:Wait promise inside for loop 【发布时间】:2018-06-09 09:21:03 【问题描述】: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 和 update 返回承诺。我怎么能说“等到两个承诺返回,然后更新 currentProduct 值”?我对 JS 很陌生...
【问题讨论】:
为什么这个问题被标记为 async-await?你想使用这个功能吗? 【参考方案1】:我会这样做:
for (let product of products)
let subs = await subscription.getAll(product._id);
await update(subs, product);
无需手动链接承诺或按索引迭代数组:)
【讨论】:
我正在尝试您的代码,我认为它更优雅。无论如何,您在 for 中缺少产品的“让”。我注意到,因为我收到了 UnhandledPromiseRejectionWarning ...您能否编辑您的代码并添加一些内容来处理承诺拒绝?提前谢谢了。编辑:没关系我应该使用 try/catch...【参考方案2】:您可能想要跟踪您已处理的产品,因为当一个产品失败时,您不知道有多少成功,并且您不知道要纠正什么(如果回滚)或重试。
异步“循环”可以是递归函数:
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
)
)
【讨论】:
【参考方案3】:如果您可以使用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);
*/
或者,如果你只能使用普通的 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))
在第二个 sn-p 中,循环只是设置了整个链,但不会立即执行 .then
内的代码。您的 getAll
函数将在之前的每个函数依次解决(这是您想要的)之前运行。
【讨论】:
如果你使用await
,你也可以使用它来代替then
调用
@Bergi 你是对的。您可以使用 await 来获取 getAll
的结果,然后将其传递给下一行的 update
并使用另一个 await。但是那里的东西仍然有效,众所周知,我会混合和匹配我的等待和然后。我想说,他喜欢哪种风格取决于 OP 的判断。
@Jumpa 我已经编辑了帖子以包含一对等待的示例,请参阅第一个 sn-p 中的评论部分。
promise 链使用递归将是最简单的,而使用 reduce 会更简单(请参阅我的答案)。该答案还可以解决或拒绝某些理智的事情,尤其是在被拒绝时,因为您需要知道它走了多远。
@neustart47 由于每个“then”链接最后一个,只需在循环完成后将其添加到 Promise 链的 .then
中。例如,循环之后:promiseChain.then(() => /* do your thing */)
以上是关于在 for 循环中等待 promise的主要内容,如果未能解决你的问题,请参考以下文章
当我使用 for 循环时,为啥我的代码没有在 forEach 中等待? [复制]