繁体   English   中英

Node.js的异步调用和递归

[英]Asynchronous Calls and Recursion with Node.js

我希望在完全完成递归函数后执行回调,该递归函数可以持续一段不确定的时间。 我正在努力解决异步问题,希望能在这里得到一些帮助。 使用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();
});

似乎一旦我的代码在嵌套请求中超过for循环,它就会继续执行请求并在递归调用仍在进行时结束初始函数调用。 我希望它不会完成最高级别的迭代,直到所有嵌套的递归调用完成(我无法知道有多少)。

任何帮助是极大的赞赏!

在您的示例中,您没有递归调用。 如果我理解正确你想说recurse(point, otherFunc); 是递归调用的开始。

然后回到递归调用的定义(你没有在帖子中显示)并执行此操作(为递归结束时调用的回调函数添加第三个参数;调用者将其作为参数传递):

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
}

然后在您发布的原始代码中,改为进行此调用(在最内部):

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
});

花一些时间试着理解我的解释。 当你明白,那么你就会知道节点。 这是一篇文章中的节点哲学。 我希望很清楚。 您的第一个示例应如下所示:

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中,人们也会返回错误值,以便调用者知道被调用的函数是否已成功完成。 这里没有什么大不了的。 而不是只返回results人们打电话给表格

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);
            }
        });
    });
};

请注意,我假设get为“BBB”的要求总是跟着一个get为“CCC”的要求。 换句话说,您没有为具有注释的递归调用隐藏返回点。

通常,当您编写递归函数时,它会执行某些操作 ,然后调用自身或返回。

您需要在递归函数的范围内定义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);

我想你可能会发现caolan / async很有用。 特别async.waterfall 它允许您从另一个回调中传递结果,完成后,对结果执行某些操作。

例:

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
});

如果递归函数是同步的,只需在下一行调用回调函数:

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);
  });
};

从此示例构建代码:

var udpate = function (callback){
    //Do stuff
    callback(null);
}

function doUpdate() {
    update(updateDone)
}

function updateDone(err) {
    if (err)
        throw err;
    else
        doUpdate()
}

doUpdate();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM