[英]async.waterfall inside a for loop escapes the for loop
On a Form Action
of type POST
, we fetch all the values in Node.JS/Express
and try saving it into MongoDB
. 在POST
类型的Form Action
上,我们获取Node.JS/Express
所有值,然后尝试将其保存到MongoDB
。
A hidden field determines the length of a property from the frontend javascript and it's value is updated as the hidden field's value. 隐藏字段确定前端javascript中属性的长度,并且其值将更新为隐藏字段的值。
This length is used in the backend (Node) to iterate over a list of items. 在后端(节点)中使用此长度来迭代项目列表。
I have a async.waterfall
function and a for loop
running inside it like this. 我有一个async.waterfall
函数和一个像这样在其中运行的for loop
。
async.waterfall([
function(callback){
var itemLength = req.body.itemLength;
var itemProp,itemComponent;
var destination;
var destinationsArray =[];
for(var k=1; k<=itemLength; k++){
destination = new Destination({
name: req.body['destinationName'+k],
});
itemComponent = {
"itemCompProp" : req.body['itemCompProp'+k]
};
itemProp = new ItemProp({
itemComponent: itemComponent
});
itemProp.save(function(err,itemPropSaved){
destination.newProperty = itemPropSaved._id
destination.save(function(err,destinationSaved){
if(err){
console.log("Error== " + err);
}
else{
destinationsArray.push(destinationSaved._id);
}
});
});
}// End of For
callback(null,destinationsArray);
},
function(destinationsArray,callback){
var brand = new Brand({
name : req.body.brandName,
});
brand.save(function(err,brandSaved){
if(err){
console.log("Error== " + err);
}else{
console.log('Brand Saved');
}
});
callback(null);
}
], function (err, status) {
if(err){
req.flash('error', {
msg: 'Error Saving Brands'
});
console.log("Error : " + err);
}
else{
console.log("Brand Saved.");
req.flash('success', {
msg: 'Brand Successfully Added!'
});
}
});
res.redirect('/redirectSomewhere');
When we run this, The destinationsArray
is returned first as null
, as opposed to going through the for loop
and then returning the proper value of destinationsArray
over a length ( itemLength
) of destinations. 当我们运行此命令时, destinationsArray
首先返回null
,而不是通过for loop
,然后在destinationsArray
的长度( itemLength
)上返回destinationsArray
的正确值。
We want the process to be synchronous. 我们希望过程是同步的。 We also tried using a closure wrapping the for Loop
but to no avail. 我们还尝试使用将for Loop
包装起来的闭包for Loop
但无济于事。
We can't use a async.eachSeries
instead of the for Loop
as I am just iterating over a numeric property and we don't have any documents to iterate over
我们不能使用async.eachSeries
而不是for Loop
因为我只是在数值属性上进行迭代,并且没有任何documents to iterate over
Any feasible solution to run a for Loop
inside a async.waterfall
? 在async.waterfall
内运行for Loop
可行解决方案吗?
Cheers and Thanks in Advance. 预先加油和感谢。
There are few problems with the code you have there: 您那里的代码几乎没有问题:
save() is asynchronous. save()是异步的。 Regular for loop will just continue without waiting for all save() calls to finish. 常规的for循环将继续,而无需等待所有save()调用完成。 That's why destinationsArray is empty. 这就是为什么destinationsArray为空的原因。 As you said, you cannot use async.eachSeries() since you're iterating through numeric property. 如您所说,您不能使用async.eachSeries(),因为您要遍历数字属性。 However, you're on the right track there. 但是,您在那儿走对了。 Async.whilst() does just that. Async.whilst()就是这样做的。 Here is the revised code with Async.whilst() and proper calling locations of the callbacks: 这是带有Async.whilst()的修改后的代码以及回调的正确调用位置:
async.waterfall([
function(callback){
var itemLength = req.body.itemLength;
var itemProp,itemComponent;
var destination;
var destinationsArray =[];
var k = 1; // 1st part of for loop: for(k=1; k<=itemLength; k++)
async.whilst(
function() {
return k <= itemLength; // 2nd part of for loop: for(k=1; k<=itemLength; k++)
},
function(whilstCb) {
destination = new Destination({
name: req.body['destinationName'+k]
});
itemComponent = {
"itemCompProp" : req.body['itemCompProp'+k]
};
itemProp = new ItemProp({
itemComponent: itemComponent
});
itemProp.save(function(err,itemPropSaved){
destination.newProperty = itemPropSaved._id
destination.save(function(err,destinationSaved){
if(err){
console.log("Error== " + err);
} else {
destinationsArray.push(destinationSaved._id);
}
k++; // 3rd part of for loop: for(k=1; k<=itemLength; k++)
whilstCb(null);
});
});
},
function(err) {
// It gets here once the loop is done
console.log(destinationsArray); // This array should have all the values pushed
callback(null, destinationsArray);
}
);
},
function(destinationsArray,callback){
var brand = new Brand({
name : req.body.brandName
});
brand.save(function(err,brandSaved){
if(err){
console.log("Error== " + err);
} else {
console.log('Brand Saved');
}
callback(null);
});
}
], function (err, status) {
if(err){
req.flash('error', {
msg: 'Error Saving Brands'
});
console.log("Error : " + err);
} else {
console.log("Brand Saved.");
req.flash('success', {
msg: 'Brand Successfully Added!'
});
}
res.redirect('/redirectSomewhere');
});
The issue has to do with callback(null, destinationsArray);
这个问题与callback(null, destinationsArray);
getting called outside the for loop
without checking first to see the loop has been finished. 在for loop
外被调用而无需先检查循环是否完成。
Try replacing callback(null, destinationsArray);
尝试替换callback(null, destinationsArray);
with something like this: 像这样:
if (itemLength > 0 && destinationsArray.length === k - 1) {
callback(null, destinationsArray);
} else {
callback(true);
}
The above checks to make sure the destination.save()
gets completed the proper number of times successfully. 上面的检查可确保destination.save()
成功完成正确的次数。
I actually prefer the method proposed by djskinner. 我实际上更喜欢djskinner提出的方法。 However, because of the console.log()
that occurs when there is a save()
error, the callbacked destinationsArray
could possibly hold the incorrect number of items. 但是,由于在发生save()
错误时会发生console.log()
,所以回调的destinationsArray
可能容纳了错误数量的项目。 To fix this, you could make sure to replace the console.log("Error== " + err);
要解决此问题,您可以确保替换console.log("Error== " + err);
with something like callback(err)
to end the waterfall with the error returned. 用类似callback(err)
类的东西来结束瀑布,并返回错误。 In addition, the k === itemLength
check doesn't properly account for the correct number of items that should be saved. 此外, k === itemLength
检查未正确说明应保存的正确项目数。 This should be replaced with k === destinationsArray.length
. 应该用k === destinationsArray.length
代替。
I made modifications to fix this and posted an updated version below. 我进行了修改以解决此问题,并在下面发布了更新版本。
destination.save(function(err, destinationSaved){
if (err) {
callback(err);
}
else {
destinationsArray.push(destinationSaved._id);
if (k === destinationsArray.length) {
callback(null, destinationsArray);
}
}
});
--EDIT-- I really like the solution that Ben posted using whilst()
. -编辑-我真的很喜欢Ben使用while whilst()
发布的解决方案。 This allows the creation of a loop where the iterations runs serially. 这样就可以创建一个循环,在该循环中迭代将顺序运行。 For more info, view the npm page here . 有关更多信息,请在此处查看npm页面。
Its not so much the for loop that is causing you problems but that save
is an asynchronous operation. 它并不是因为for循环引起您的问题,而是save
是异步操作。 The for loop completes and the callback is executed before any of the save
callbacks have had chance to complete. for循环完成,并且在任何save
回调都有机会完成之前执行回调。
What you want to do is call the async.waterfall
callback after all the destination save callbacks have been executed. 您要执行的操作是在执行所有目标保存回调之后,调用async.waterfall
回调。 Something like: 就像是:
destination.save(function(err,destinationSaved){
if(err){
console.log("Error== " + err);
} else {
destinationsArray.push(destinationSaved._id);
if (k === itemLength) {
// all destination callbacks have completed successfully
callback(null, destinationsArray);
}
}
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.