节点,异步编程,回调地狱

时间:2013-10-28 16:13:43

标签: javascript node.js asynchronous

我正在尝试理解回调和异步编程,但我遇到了一些麻烦。

这里有一些伪代码:

var lines = [];
var arrayOfFeedUrls = [url1,url2,...];

function scrape(url){
    http.get(url, function(res) {
        res.pipe(new FeedParser([options]))
        .on('readable', function () {
            var stream = this, item;
            while (item=stream.read()) {
                line = item.title;
                lines.push(line);
            }
        });
    });
});

for (i in arrayOfFeedUrls){
    scrape(arrayOfFeedUrls[i];
}
console.log(lines.length);

它显然返回0,因为scrape函数是异步执行的。我理解的很多,但我尝试了许多错综复杂的方法,无法弄清楚如何正确地编写它。任何帮助/解释将不胜感激。我已经阅读了(我还在阅读)很多教程和示例,但我认为获得它的唯一方法是自己编写一些代码。如果我解决了这个问题,我会发布答案。

5 个答案:

答案 0 :(得分:2)

您可以查看this article以了解Node中的介绍,这可能有助于您更好地了解Node中的异步编程。

就异步编程而言,async是Node用户区中非常流行的模块,它可以帮助您轻松编写异步代码。例如(未经测试的伪代码):

function scrape (done) {
  http.get(url, done);
}

function parse (res, done) {
  var lines = [];

  res.pipe(new FeedParser([options]))
        .on('readable', function () {
            var stream = this, item;
            while (item=stream.read()) {
                line = item.title;
                lines.push(line);
            }
        })
        .on('end', function () {
          done(null, lines);
        });
}

function done (err, lines) {
  if (err) { throw err; }

  console.log(lines.length);
}

async.waterfall([scrape, parse], done);

答案 1 :(得分:1)

这取决于您是想要并行扫描还是串联扫描所有网址。

如果你要连续拍摄,你应该把它想象成这样:

从第一个网址开始。刮。在回调中,刮掉下一个url。在回调中,刮掉下一个网址。

这会给你所说的臭名昭着的回调,但至少这是原则。图书管理员喜欢async等会让人头疼不已。

答案 2 :(得分:1)

以这种方式编写异步调用时,您希望链接到最后的函数和指令(例如console.log(lines.length);)也必须是回调函数。例如,尝试这样的事情:

var lines = [];
var arrayOfFeedUrls = [url1,url2,...];

function scrape(url){
    http.get(url, function(res) {
        res.pipe(new FeedParser([options]))
        .on('readable', function () {
            var stream = this, item;
            while (item=stream.read()) {
                line = item.title;
                lines.push(line);
                done();
            }
        });
    });
});

for (i in arrayOfFeedUrls){
    scrape(arrayOfFeedUrls[i];
}
function done () {
    if (lines.length == arrayOfFeedUrls.length) {
        console.log(lines.length);
    }
}

您可能还想查看promises,这是回调的另一种编程风格,旨在避免回调地狱。

答案 3 :(得分:1)

不得不承认我对node.js很新,并努力克服回调的东西。在我有限的经验中,向回调函数添加一个参数可能就是诀窍。难题是,哪个参数?

在您的示例中,如果函数scrape有一个额外的布尔“lastOne”,那么它可以调用console.log(行)本身。或者,如果它理解空url意味着停止。 然而,我不认为即便如此,因为我不确定一切都会按顺序完成。如果第二个URL需要永远,最后一个URL可能先完成,对吧??? (你可以尝试一下)。换句话说,我仍然不知道要添加哪个参数。遗憾...

似乎更可靠的是将计数器设置为urls.length,并且scrape()每次都减少它。当计数器达到0时,它知道整个过程已完成,并且应该记录(或做任何事情)结果。我不是100%肯定在哪里宣布这个柜台。来自Java我仍然不知道什么是静态全局,什么是实例,无论......

现在,一个真正的蓝色node.jser会传递一个函数来执行任何作为scrape()的额外参数,以便您可以执行console.log()之外的其他操作。 :-)但我会支持零检查。

稍微详细说明,将callWhenDone参数添加到scrape(),然后添加(在所有嵌套中的某处!)

if (--counter <= 0)
  callWhenDone (lines);

答案 4 :(得分:1)

好的,所以这就是我如何解决问题,随意评论并告诉我它是否正确。

var lines = [];
var arrayOfFeedUrls = [url1,url2,...];

function scrape(array){
    var url = array.shift();
    http.get(url, function(res) {
        res.pipe(new FeedParser([options]))
        .on('readable', function () {
            var stream = this, item;
            while (item=stream.read()) {
                line = item.title;
                lines.push(line);
            }
        }).on('end', function () {
            if(array.length){
                scrapeFeeds(array);
            }
        });
    });
});

scrapeFeeds(array);

感谢所有的答案,我正在寻找更深入的异步,因为我有更复杂的事情要做。让我知道您对我的代码的看法,它始终有用。