javascript JS等待回调在循环内完成
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24543624/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JS wait for callback to finish inside a loop
提问by Cereal Killer
I have in my node js app a for loop; inside this loop on each iteration a mysql query can be executed (not always, depends); the query is async and I get the result in the success callback; but I need that each iteration of the for loop waits for the callback to finish (if needed):
我的节点 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;
}
If id != undefined, the query is executed but the for loop doesn't wait for the success callback and the function returns an incomplete resArray.
如果 id != undefined,则执行查询,但 for 循环不会等待成功回调并且函数返回不完整的 resArray。
How can I do (or how can I reorganize the code) to make this work? (I'm working with node js and I don't have the possibility to use jQuery).
我该怎么做(或者我如何重新组织代码)来完成这项工作?(我正在使用节点 js,我无法使用 jQuery)。
采纳答案by Cereal Killer
Thanks to Amadan suggestions i got rid of this using a mixed method (with promises and a counter);
感谢 Amadan 的建议,我使用混合方法(使用承诺和计数器)摆脱了这个问题;
I'm really interested in your opinion about this and if you have suggestions to improve it;
我对你对此的看法非常感兴趣,如果你有改进它的建议;
First I've added to the project the q library (npm install q) for using promises. Thanks to q i've been able to wrap sequelize.query (callback-based) within a function that returns a promise; a counter for promises keeps in memory the number of promises still pending and the wrapping function executeQuery decremets it when each promise is resolved;
首先,我将 q 库(npm install q)添加到项目中以使用 Promise。感谢 q,我已经能够将 sequelize.query(基于回调)包装在一个返回承诺的函数中;承诺的计数器在内存中保留未决的承诺数量,当每个承诺得到解决时,包装函数 executeQuery 将其递减;
(the for loop in cycle() function executes 4 loops; it executes 2 times the query and 2 times a simple log; the goal is that the 'finished' log is sent to the screen only after for loop ends and all promises are resolved
(cycle() 函数中的 for 循环执行 4 次循环;它执行 2 次查询和 2 次简单日志;目标是仅在 for 循环结束并且所有承诺都得到解决后才将“完成”的日志发送到屏幕
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();
------------------ UPDATE ------------------------------------------------------------
- - - - - - - - - 更新 - - - - - - - - - - - - - - - - -----------------------------
Following hugomg suggestion I've updated the code with this that is more clean: I push every promise in an array and then use Q.all to wait for all of them to be resolved
遵循 Hugomg 的建议,我更新了代码,使其更加干净:我将每个承诺推送到一个数组中,然后使用 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();
回答by Amadan
You need to change your way of thinking. If calculate
calls an asynchronous method, the only way you're getting its results is not by return
, but by calling another callback. In node.js, you mustn't think of a function as a black box that takes an input and returns an output; you need to think of it as a step in a process that has a certain future. The question you need to ask yourself is - "I got these results; what then?"
你需要改变你的思维方式。如果calculate
调用异步方法,则获取其结果的唯一方法不是通过return
,而是通过调用另一个回调。在 node.js 中,您不能将函数视为接受输入并返回输出的黑盒子;您需要将其视为具有特定未来的流程中的一个步骤。你需要问自己的问题是——“我得到了这些结果;然后呢?”
Just like with sequelize.query
, where you tell it "do this query, and then do thiswith the results", you need to be able to call calculate
and tell it "go do those queries, and then do thiswith their results". You're not getting a return from sequelize.query
- and you shouldn't be trying to return something from calculate
, either.
就像 with 一样sequelize.query
,您告诉它“执行此查询,然后使用结果执行此操作”,您需要能够调用calculate
并告诉它“执行这些查询,然后使用其结果执行此操作”。你没有得到回报sequelize.query
- 你也不应该试图从 返回一些东西calculate
。
Promises would work great, but without relying on them, you can just count. There are four changes in code below:
Promises 会很好用,但不依赖它们,你可以数数。下面的代码有四处变化:
// 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
}
and then you can replace the hypothetic and non-functional
然后你可以替换假设的和非功能性的
var resArray = calculate();
console.log("Here's your results:", resArray);
with
和
calculate(function(resArray) {
console.log("Here's your results:", resArray);
});
EDIT: put the countdown into done
handler to account for possible errors.
编辑:将倒计时放入done
处理程序以解决可能的错误。