我在我的节点js app中有一个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)。
答案 0 :(得分:2)
感谢Amadan建议我使用混合方法(使用promises和counter)摆脱了这个;
我真的对您对此的看法感兴趣,如果您有改进建议,
首先,我在项目中添加了q库(npm install q)以使用promises。 感谢q我已经能够在返回promise的函数中包装sequelize.query(基于回调); promises的计数器在内存中保留了仍然未决的promise数量,并且包装函数executeQuery在每个promise都被解析时会对它进行下限;
(cycle()函数中的for循环执行4个循环;它执行2次查询,2次执行简单日志; 目标是只有在循环结束并且所有承诺都已解决之后,“已完成”的日志才会被发送到屏幕
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();
答案 1 :(得分:1)
你需要改变你的思维方式。如果calculate
调用异步方法,则获取结果的唯一方法不是return
,而是通过调用另一个回调。在node.js中,你不能把一个函数想象成一个接收输入并返回输出的黑盒子;你需要把它看作是一个有一定未来的过程中的一步。你需要问自己的问题是 - &#34;我得到了这些结果;那么呢?&#34;
就像使用sequelize.query
一样,你告诉它&#34;做这个查询,然后用结果&#34; 这个,你需要能够调用{ {1}}然后告诉它&#34;去做那些查询,然后用他们的结果&#34; 这个。您未能从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);
});
处理程序以解决可能的错误。