Javascript Node.js的异步调用和递归
我希望在递归函数完全完成后执行回调,该函数可以持续不确定的时间。我正在努力解决异步问题,希望在这里得到一些帮助。使用Javascript Node.js的异步调用和递归,javascript,node.js,asynchronous,recursion,Javascript,Node.js,Asynchronous,Recursion,我希望在递归函数完全完成后执行回调,该函数可以持续不确定的时间。我正在努力解决异步问题,希望在这里得到一些帮助。使用请求模块的代码如下: var start = function(callback) { request.get({ url: 'aaa.com' }, function (error, response, body) { var startingPlace = JSON.parse(body).id; recurse(startingPlace, c
请求
模块的代码如下:
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();
});
看起来,一旦我的代码通过嵌套请求中的for
循环,它就会继续离开请求,并在递归调用仍在进行时结束初始函数调用。我希望它在所有嵌套递归调用完成之前(我无法知道有多少嵌套递归调用)不会完成最高级别的迭代
非常感谢您的帮助 我想你可能会觉得有用。请特别关注异步瀑布。它将允许您从另一个回调传递结果,完成后,对结果进行处理
例如:
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
});
通常,当您编写递归函数时,它会执行一些操作,然后调用自身或返回
您需要在递归函数的范围内定义callback
(即recurse
而不是start
),并且需要在通常返回的点调用它
因此,假设的示例如下所示:
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);
如果递归函数是同步的,只需在下一行调用回调:
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();
});
};
否则,您需要在递归函数中保留对回调的引用
将回调作为参数传递给函数,并在完成时调用它
var start = function(callback) {
request.get({
url: 'aaa.com'
}, function (error, response, body) {
var startingPlace = JSON.parse(body).id;
recurse(startingPlace, otherFunc, callback);
});
};
在您的示例中,没有递归调用。如果我理解正确,你想说递归(point,otherFunc)代码>是递归调用的开始
然后,只需返回递归调用的定义(您在文章中没有显示)并执行此操作(为递归结束时要调用的回调函数添加第三个参数;调用方将其作为参数传递):
然后在您发布的原始代码中,改为进行此调用(在最内部部分):
花点时间试着理解我的解释。当你明白了,你就会知道这个节点。这是一篇文章中的节点哲学。我希望这是清楚的。您的第一个示例应该如下所示:
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();
});
});
};
以下仅是您感兴趣时的附加信息。否则,您将使用上述设置。
但是,通常在node.js中,人们也会返回一个错误值,以便调用方知道调用的函数是否已成功完成。这里没有什么大秘密。人们不只是返回结果
而是调用表单
return callback_one(null, val);
然后,在其他功能中,您可以:
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()
});
更新我的建议
这是我对递归函数的建议,但在此之前,您需要定义自己的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);
}
});
});
};
函数myGet(a,回调){
获取(函数(错误、响应、正文){
var nextPlace=JSON.parse(body.place);
return callback(null,nextPlace);//null表示没有错误,并将nextPlace返回到async
});
}
var recurse=函数(起始位置,回调2){
请求({
url:'bbb'
},功能(错误1,响应1,主体1){
//把尸体存放在这些功能之外的某个地方
//提出第二个请求
请求({
url:'ccc'
},功能(错误2,响应2,车身2){
var anArray=JSON.parse(body2.stuff);
如果(错误){
//要为数组的每个元素调用的函数是“get”。
//所以,准备这些调用,但还需要传递不同的参数
//这就是“bind”出现在图片和我之前给出的链接中的地方。
平行球的var=[];
对于(变量i=0;i
请注意,我假设'bbb'的get
请求后面总是跟着'ccc'的get
请求。换句话说,您没有隐藏具有注释的递归调用的返回点。根据此示例构建代码:
var udpate = function (callback){
//Do stuff
callback(null);
}
function doUpdate() {
update(updateDone)
}
function updateDone(err) {
if (err)
throw err;
else
doUpdate()
}
doUpdate();
使用ES6,“ES6-延迟”和“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");
});
你需要给材料命名,你需要一个外部包装器函数来包装显示的部分,你可以收集结果/监控进度。你需要解决很多问题。(1) 更改递归(nextPlace)代码>到递归(下一个位置,回调)代码>(2)如果它不是测试中的数组,递归将停止,因此,就在If(anArray){…}
写下回调()之后代码>(3)完全删除回调()如果您让任何异步事件发生,或者如果执行停止,您应该分别传递或调用(这次使用return callback();
)回调函数。你准备好了。在任何情况下,执行(1)-(3),您将让我们知道发生了什么。我只需要在所有递归循环完成后调用最后一个函数
var udpate = function (callback){
//Do stuff
callback(null);
}
function doUpdate() {
update(updateDone)
}
function updateDone(err) {
if (err)
throw err;
else
doUpdate()
}
doUpdate();
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");
});