[英]JS wait for callback to finish inside a loop
我的节点js应用程序中有一个for循环; 在此循环中,每次迭代都可以执行mysql查询(并非总是如此,取决于); 查询是异步的,我在成功回调中得到结果; 但我需要for循环的每次迭代都等待回调完成(如果需要):
function calculate() {
var resArray = [];
//[...]
for (i = 0; i < tables.length; i++) {
if (id !== undefined) {
var queryString = ... //build query
sequelize.query(queryString).success(function(result) { //executes query
id = result[0].id;
//do stuff with id returned by the query
//...
//resArray.push( //push in resArray the result of stuff done above )
});
}
else {
//do other kind of stuff and push result in resArray
}
}
return resArray;
}
如果id!= undefined,则执行查询,但是for循环不等待成功回调,并且该函数返回不完整的resArray。
我该怎么做(或如何重组代码)来完成这项工作? (我正在使用节点js,并且无法使用jQuery)。
多亏了阿玛丹(Amadan)的建议,我使用了混合方法(带有保证和反击)来摆脱了这种情况。
我真的很感兴趣您对此的意见,以及您是否有改进建议。
首先,我已将q库(npm install q)添加到项目中以使用promises。 多亏了q,我才能够将sequelize.query(基于回调)包装在返回promise的函数中; 一个promise计数器将仍在等待中的promise数量保留在内存中,并且包装函数executeQuery在解析每个promise时将其减少;
(for cycle in cycle()函数执行4个循环;它执行2次查询和2次简单日志;目标是“完成的”日志仅在for循环结束且所有promise均被解析后才发送到屏幕
var id = req.params.id;
var counter = 0;
var endCycle = false;
var queryString = 'SELECT * FROM offers WHERE id = ' + id;
function query () {
var deferred = Q.defer();
sequelize.query(queryString).success(function(result) {
console.log('obtained result');
deferred.resolve(result);
});
return deferred.promise;
}
function executeQuery() {
query().then(function() {
console.log('after');
counter --;
console.log(counter);
finished();
});
}
function finished() {
if ((counter === 0) && (endCycle)) {
console.log('finished');
endCycle = false;
}
}
function cycle() {
var result;
for (i = 0; i <= 3; i ++) {
if (i > 1) {
counter ++;
executeQuery();
}
else {
console.log('else');
}
}
endCycle = true;
finished();
}
cycle();
遵循hugomg的建议,我已经使用更干净的代码更新了代码:我将每个promise推送到数组中,然后使用Q.all等待所有这些被解决
var id = req.params.id;
var promisesArray = [];
var endCycle = false;
var queryString = 'SELECT * FROM offers WHERE id = ' + id;
function query () {
var deferred = Q.defer();
sequelize.query(queryString).success(function(result) {
console.log('obtained result');
deferred.resolve(result);
finished();
});
promisesArray.push(deferred.promise);
}
function finished() {
if (endCycle) {
endCycle = false;
Q.all(promisesArray).then(function() {
console.log('finished');
});
}
}
function cycle() {
var result;
for (i = 0; i <= 3; i ++) {
if (i > 1) {
query();
}
else {
console.log('else');
}
}
endCycle = true;
finished();
}
cycle();
您需要改变思维方式。 如果calculate
调用异步方法,则获取其结果的唯一方法不是通过return
,而是通过调用另一个回调。 在node.js中,您不应将函数视为一个接受输入并返回输出的黑盒子。 您需要将其视为具有一定前途的过程中的一步。 您需要问自己的问题是-“我得到了这些结果;然后呢?”
就像sequelize.query
,您要告诉它“执行此查询,然后对结果执行此操作 ”,您需要能够调用calculate
并告诉它“执行这些查询,然后对结果执行此操作 ”。 。 你没有得到来自返回sequelize.query
-你不应该试图返回从一些calculate
,无论是。
承诺会很好地工作,但是如果不依靠它们,您可以指望一下。 下面的代码有四个更改:
// HERE: take a callback parameter
function calculate(callback) {
var outstandingRequests = 0;
var resArray = [];
//[...]
for (i = 0; i < tables.length; i++) {
if (id !== undefined) {
var queryString = ... //build query
// HERE: count how many responses you are expecting
outstandingRequests++;
sequelize.query(queryString).success(function(result) { //executes query
id = result[0].id;
//do stuff with id returned by the query
//...
//resArray.push( //push in resArray the result of stuff done above )
// HERE: check if all requests are done
// if so, the array is as full as it will ever be
// and we can pass the results on
}).done(function() {
if (!(--outstandingRequests)) {
callback(resArray);
}
});
}
else {
//do other kind of stuff and push result in resArray
}
}
// HERE: return is useless in asynchronous code
}
然后您可以替换假设的和非功能的
var resArray = calculate();
console.log("Here's your results:", resArray);
同
calculate(function(resArray) {
console.log("Here's your results:", resArray);
});
编辑:将倒数放入done
处理程序中以解决可能的错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.