在此功能内:
function Example(array, callback){
var toReturn = [];
// variable 'array' has 2 elements in this case
array.forEach(function(el){
MongooseModel.AsyncFunction(el, function(err, doc){
if(err || !doc){ return callback('Error'); }
toReturn.push(doc)
});
});
return callback(null, toReturn)
}
有几点需要注意:
toReturn.push(doc)
没有工作,最后一次回调是空的
返回数组。这是为什么? return callback('Error')
。始终会调用return callback(null, toReturn)
。另外,如果我在异步函数上面放回调,就像这样:
function Example(array, callback){
var toReturn = [];
// variable 'array' has 2 elements in this case
array.forEach(function(el){
// another callback here
return callback('Error')
MongooseModel.AsyncFunction(el, function(err, doc){
if(err || !doc){ return callback('Error'); }
toReturn.push(doc)
});
});
return callback(null, toReturn)
}
脚本崩溃,因为已经调用了多个回调。为什么每个人都这样做以及如何避免它?
答案 0 :(得分:8)
toReturn.push(doc)不起作用,在最后一个回调中返回一个空数组。那是为什么?
您在callback
有机会执行MongooseModel.AsyncFunction
之前致电function(err,doc)
。
如果出现错误,或者!docs为true,则永远不会调用return callback('Error')。始终调用return callback(null,toReturn)。
在安排异步功能后立即调用成功返回。 callback('Error')
之后可能会稍后调用MongooseModel.AsyncFunction
。
脚本崩溃,因为已经调用了多个回调
这正是你要求的行为!您的第二个代码字面上说:“对于每个元素,请调用一次回调”。
总而言之,我认为你唯一错误的是调用回调有点类似于从函数返回。
事实并非如此!您的函数Example
应该安排一些异步事件发生,然后立即返回(不返回任何内容),但承诺callback(error)
或callback(null, success)
将在稍后的某个时间被称为 强>
因此,永远不要说return callback()
- 仅callback()
会这样做。当您有一些数据要调用callback
时,函数Example
将已经完成执行。最终调用callback
的函数将匿名函数传递给MongooseModel.AsyncFunction
,而不是Example
本身。
您可以尝试这样的方法:
function Example(array, callback){
var toReturn = [];
var previousError = false;
array.forEach(function(el){
MongooseModel.AsyncFunction(el, function(err, doc){
if (previousError) {
return;
}
if(err || !doc) {
previousError = true;
callback('Error');
}
toReturn.push(doc)
if (toReturn.length === array.length) {
// that was the last push - we have completed
callback(null, toReturn);
}
});
});
}
这里发生的事情:
每次AsyncFunction
完成时,都会汇总结果。如果那是最后一个,您最终可以调用callback
并传递整个数据。 (否则,我们正在等待更多功能,所以没有callback
)
如果在此过程中某处出现错误,我们会立即报告错误,但请注意已经报告错误,以便进一步执行已安排的AsyncFunction
特别做任何事情。这可以防止您callback
被调用两次或更多次。
请注意 - toReturn
内的元素顺序是随机的,具体取决于首先完成的异步任务。
哦,是的。这就是为什么我们不再做回调了。有一个名为promises
的模式使得使用异步回调意大利面更加容易,我建议在继续前进之前先阅读一下这个主题。
使用promises,这种代码看起来像:
function Example(array) {
var promises = array.map(function(el) {
return MongooseModel.AsyncFunction(el);
});
return Promise.all(promises);
}
它更短,并且在输出项的错误处理或排序方面也没有任何问题,就像我们之前的例子一样。
用法很简单 - 而不是这个
Example([1, 2, 3], function(error, results) {
if (error !== null) {
// ...
} else {
// ...
}
})
你打电话是这样的:
Example([1, 2, 3]).then(
function(results) {
// ...
},
function(error) {
// ...
}
);
哦,这依赖于MongooseModel.AsyncFunction
返回一个承诺,但Mongoose already does this大多数库也是如此。如果库没有,您可以轻松添加承诺支持(例如,请参阅NodeRedis)。
如何在返回之前对AsyncFunction
的每个结果执行某些操作?
很容易!
function Example(array) {
var promises = array.map(function(el) {
return MongooseModel.AsyncFunction(el).then(function(doc) {
return doc + "some modifications";
});
});
return Promise.all(promises);
}
或者如果您需要其他错误处理:
function Example(array) {
var promises = array.map(function(el) {
return MongooseModel.AsyncFunction(el).then(function(doc) {
if (theresSomethingWrongWith(doc) {
return Promise.reject(new Error("ouch!"));
}
return doc + "some modifications";
});
});
return Promise.all(promises);
}
想一想将被拒绝的承诺归类为类似于提出异常的东西 - 它会自然地冒出一直到Example
的回归承诺。
答案 1 :(得分:2)
基本上,您的功能按以下顺序执行:
array
要执行多个异步调用并返回结果,您需要等待所有这些调用完成。我通常在库async的帮助下解决这个问题,它甚至可以控制异步调用的执行方式(串行或并行)。例如,您可以使用异步函数数组调用async.parallel:
async.parallel(array.map(function() {
return MongooseModel.AsyncFunction(...)
}), function(err, results) {
// all the async calls are finished, do something with the results
});
答案 2 :(得分:0)
你应该使用promises,因为你需要在返回结果之前等待所有异步调用。这是一个例子:
function Example(array, callback){
new Promise(function(resolve, reject) {
var toReturn = [];
array.forEach(function(el){
MongooseModel.AsyncFunction(el, function(err, doc){
if(err || !doc) {
return reject();
}
toReturn.push(doc);
if (toReturn.length === array.length) {
resolve(toReturn);
}
});
});
})
.then(callback)
.catch(function() {
callback('error');
});
}
在这里,您可以阅读有关承诺的更多信息:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise