Javascript Node.js 的异步调用和递归
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26515671/
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
Asynchronous Calls and Recursion with Node.js
提问by AvocadoRivalry
I'm looking to execute a callback upon the full completion of a recursive function that can go on for an undetermined amount of time. I'm struggling with async issues and was hoping to get some help here. The code, using the requestmodule, is as follows:
我希望在递归函数完全完成后执行回调,该函数可以持续不确定的时间。我正在为异步问题而苦苦挣扎,希望能在这里得到一些帮助。使用该request模块的代码如下:
var start = function(callback) {
request.get({
url: 'aaa.com'
}, function (error, response, body) {
var startingPlace = JSON.parse(body).id;
recurse(startingPlace, callback);
});
};
var recurse = function(startingPlace, callback) {
request.get({
url: 'bbb'
}, function(error, response, body) {
// store body somewhere outside these funtions
// make second request
request.get({
url: 'ccc'
}, function(error, response, body) {
var anArray = JSON.parse(body).stuff;
if (anArray) {
anArray.forEach(function(thing) {
request.get({
url: 'ddd'
}, function(error, response, body) {
var nextPlace = JSON.parse(body).place;
recurse(nextPlace);
});
})
}
});
});
callback();
};
start(function() {
// calls final function to print out results from storage that gets updated each recursive call
finalFunction();
});
It seems that once my code goes past the forloop in the nested requests, it continues out of the request and ends the initial function call while the recursive calls are still going on. I want it to not finish the highest-level iteration until all the nested recursive calls have completed (which I have no way of knowing how many there are).
似乎一旦我的代码for通过嵌套请求中的循环,它就会继续请求并结束初始函数调用,而递归调用仍在进行中。我希望它在所有嵌套递归调用完成之前不完成最高级别的迭代(我无法知道有多少)。
Any help is GREATLY appreciated!
任何帮助是极大的赞赏!
回答by MightyMouse
In your example you have no recursive calls. If I understand correctly you want to say that recurse(point, otherFunc);is the beginning of a recursive call.
在您的示例中,您没有递归调用。如果我理解正确,您想说这recurse(point, otherFunc);是递归调用的开始。
Then just go back to the definition of the recursive call (which you have not shown in your post) and do this (add a third argument for a callback function to be called in the end of recursion; the caller will pass it as a parameter):
然后回到递归调用的定义(您没有在帖子中显示)并执行此操作(为要在递归结束时调用的回调函数添加第三个参数;调用者会将其作为参数传递):
function recurse(startingPlace, otherFunc, callback_one) {
// code you may have ...
if (your_terminating_criterion === true) {
return callback_one(val); // where val is potentially some value you want to return (or a json object with results)
}
// more code you may have
}
Then in the original code that you posted, make this call instead (in the inner-most part):
然后在您发布的原始代码中,改为进行此调用(在最内部):
recurse(startingPlace, otherFunc, function (results) {
// results is now a variable with the data returned at the end of recursion
console.log ("Recursion finished with results " + results);
callback(); // the callback that you wanted to call right from the beginning
});
Just spend some time and try to understand my explanation. When you understand, then you will know node. This is the node philosophy in one post. I hope it is clear. Your very first example should look like this:
花点时间试着理解我的解释。当你理解了,那么你就会知道节点。这是一篇文章中的节点哲学。我希望很清楚。您的第一个示例应如下所示:
var start = function(callback) {
request.get({
url: 'aaa.com'
}, function (error, response, body) {
var startingPlace = JSON.parse(body).id;
recurse(startingPlace, otherFunc, function (results) {
console.log ("Recursion finished with results " + results);
callback();
});
});
};
Below is only additional information in case you are interested. Otherwise you are set with the above.
如果您有兴趣,以下只是附加信息。否则,您将使用上述设置。
Typically in node.js though, people return an error value as well, so that the caller knows if the function that was called has finished successfully. There is no big mystery here. Instead of returning just resultspeople make a call of the form
但通常在 node.js 中,人们也会返回一个错误值,以便调用者知道被调用的函数是否已成功完成。这里没有什么大的秘密。而不是返回只是results人们拨打表格
return callback_one(null, val);
Then in the other function you can have:
然后在另一个功能中,您可以拥有:
recurse(startingPlace, otherFunc, function (recError, results) {
if (recErr) {
// treat the error from recursion
return callback(); // important: use return, otherwise you will keep on executing whatever is there after the if part when the callback ends ;)
}
// No problems/errors
console.log ("Recursion finished with results " + results);
callback(); // writing down `return callback();` is not a bad habit when you want to stop execution there and actually call the callback()
});
Update with my suggestion
更新我的建议
This is my suggestion for the recursive function, but before that, it looks like you need to define your own get:
这是我对递归函数的建议,但在此之前,您似乎需要定义自己的get:
function myGet (a, callback) {
request.get(a, function (error, response, body) {
var nextPlace = JSON.parse(body).place;
return callback(null, nextPlace); // null for no errors, and return the nextPlace to async
});
}
var recurse = function(startingPlace, callback2) {
request.get({
url: 'bbb'
}, function(error1, response1, body1) {
// store body somewhere outside these funtions
// make second request
request.get({
url: 'ccc'
}, function(error2, response2, body2) {
var anArray = JSON.parse(body2).stuff;
if (anArray) {
// The function that you want to call for each element of the array is `get`.
// So, prepare these calls, but you also need to pass different arguments
// and this is where `bind` comes into the picture and the link that I gave earlier.
var theParallelCalls = [];
for (var i = 0; i < anArray.length; i++) {
theParallelCalls.push(myGet.bind(null, {url: 'ddd'})); // Here, during the execution, parallel will pass its own callback as third argument of `myGet`; this is why we have callback and callback2 in the code
}
// Now perform the parallel calls:
async.parallel(theParallelCalls, function (error3, results) {
// All the parallel calls have returned
for (var i = 0; i < results.length; i++) {
var nextPlace = results[i];
recurse(nextPlace, callback2);
}
});
} else {
return callback2(null);
}
});
});
};
Note that I assume that the getrequest for 'bbb' is always followed by a getrequest for 'ccc'. In other words, you have not hidden a return point for the recursive calls where you have the comments.
请注意,我假设get对 'bbb' 的get请求总是跟在对 'ccc'的请求之后。换句话说,您没有在有注释的地方隐藏递归调用的返回点。
回答by Quentin
Typically when you write a recursive function it will do somethingand then either call itself or return.
通常,当您编写递归函数时,它会执行某些操作,然后调用自身或返回。
You need to define callbackin the scope of the recursive function (i.e. recurseinstead of start), and you need to call it at the point where you would normally return.
您需要callback在递归函数的范围内进行定义(即recurse代替start),并且需要在通常返回的位置调用它。
So, a hypothetical example would look something like:
所以,一个假设的例子看起来像:
get_all_pages(callback, page) {
page = page || 1;
request.get({
url: "http://example.com/getPage.php",
data: { page_number: 1 },
success: function (data) {
if (data.is_last_page) {
// We are at the end so we call the callback
callback(page);
} else {
// We are not at the end so we recurse
get_all_pages(callback, page + 1);
}
}
}
}
function show_page_count(data) {
alert(data);
}
get_all_pages(show_page_count);
回答by Juho Veps?l?inen
I think you might find caolan/asyncuseful. Look especially into async.waterfall. It will allow you to pass results from a callback from another and when done, do something with the results.
我想你可能会发现caolan/async很有用。特别看一下async.waterfall。它将允许您从另一个回调中传递结果,并在完成后对结果进行处理。
Example:
例子:
async.waterfall([
function(cb) {
request.get({
url: 'aaa.com'
}, function(err, res, body) {
if(err) {
return cb(err);
}
cb(null, JSON.parse(body).id);
});
},
function(id, cb) {
// do that otherFunc now
// ...
cb(); // remember to pass result here
}
], function (err, result) {
// do something with possible error and result now
});
回答by Bobby Marinoff
If your recursive function is synchronous, just call the callback on the next line:
如果您的递归函数是同步的,只需在下一行调用回调:
var start = function(callback) {
request.get({
url: 'aaa.com'
}, function (error, response, body) {
var startingPlace = JSON.parse(body).id;
recurse(startingPlace, otherFunc);
// Call output function AFTER recursion has completed
callback();
});
};
Else you need to keep a reference to the callback in your recursive function.
否则,您需要在递归函数中保留对回调的引用。
Pass the callback as an argument to the function and call it whenever it is finished.
将回调作为参数传递给函数,并在完成时调用它。
var start = function(callback) {
request.get({
url: 'aaa.com'
}, function (error, response, body) {
var startingPlace = JSON.parse(body).id;
recurse(startingPlace, otherFunc, callback);
});
};
回答by AugustoL
Build your code from this example:
从此示例构建您的代码:
var udpate = function (callback){
//Do stuff
callback(null);
}
function doUpdate() {
update(updateDone)
}
function updateDone(err) {
if (err)
throw err;
else
doUpdate()
}
doUpdate();
回答by JMadushan
With ES6, 'es6-deferred'& 'q'. You could try as following,
使用ES6,'es6-deferred'& 'q'。你可以尝试如下,
var Q = require('q');
var Deferred = require('es6-deferred');
const process = (id) => {
var request = new Deferred();
const ids =//do something and get the data;
const subPromises = ids.map(id => process(id));
Q.all(subPromises).then(function () {
request.resolve();
})
.catch(error => {
console.log(error);
});
return request.promise
}
process("testId").then(() => {
console.log("done");
});

