在nodeJS中使while循环异步

时间:2013-06-04 11:29:36

标签: javascript node.js asynchronous

Stack上有几个类似的问题,但我无法得到任何适合我的答案,我对Node和异步编程的想法都很陌生,所以请耐心等待。

我正在构建一个目前有四个步骤的刮刀:

  1. 我给它一组链接
  2. 转到每个链接,在页面上找到所有相关的img src
  3. 找到“下一页”链接,获取其href,从所述href检索dom并重复步骤#2。
  4. 所有这些img src都放入数组并返回
  5. 这是代码。 getLinks可以异步调用,但其中的while循环当前不能:

    function scrape(url, oncomplete) {
        console.log("Scrape Function: " + url);
        request(url, function(err, resp, body) {
            if (err) {
                console.log(UHOH);
                throw err;
            }
            var html = cheerio.load(body);
            oncomplete(html);
        }
        );
    }
    function getLinks(url, prodURL, baseURL, next_select) {
        var urls = [];
        while(url) {
            console.log("GetLinks Indexing: " + url);
            var html = scrape(url, function(data) {
                $ = data;
                $(prodURL).each(function() {
                    var theHref = $(this).attr('href');
                    urls.push(baseURL + theHref);
                }
                );
                next = $(next_select).first().attr('href');
                url  = next ? baseurl + next : null;
            }
            );
        }
        console.log(urls);
        return urls;
    }
    

    目前这进入了无限循环而没有刮擦任何东西。如果我将url = next ? baseurl + next : null;置于回调之外,则会出现"next" is not defined错误。

    关于如何重新设计它以使其对节点友好的任何想法?似乎,根据这个问题的本质,它需要阻塞,不是吗?

1 个答案:

答案 0 :(得分:1)

这是一种常见的模式,您希望执行循环但使用带回调的异步函数。由于您不能等待异步函数,因此不能简单地使用while循环。

一种解决方案是使用“堆栈”(或数组)。使用您要处理的初始元素填充它。当您发现要处理的更多元素时,将它们添加到此堆栈中。并递归调用函数递增索引以进行处理,直到索引超过数组的长度。

e.g。

function do_scrape( stack, this_url, callback ) {
    // get list of URLs from webpage at this_url
    ...
    stack.push( new_url ); // adding new element to array
    ...
    ...
    callback(); // process callback
}

function process_stack( stack_of_urls, idx ) {
    var this_url = stack_of_urls[idx];

    do_scrape(
        stack_of_urls,
        this_url,           
        function () {
            if ( idx + 1 < stack_of_urls.length ) {
                process_stack( stack_of_urls, (idx + 1) );
            } else {
                process.exit( 0 );
            }
        }
    );
}

var stack_of_urls = [ "http://www.yahoo.com/" ];
process_stack( stack_of_urls, 0 ); // start from index zero

注意有很多方法可以解决这个问题。为了提高效率,您可以从堆栈中删除已处理的元素。您还可以选择是从头到尾,还是从头到尾等处理堆栈。最后请注意,如果您不在do_scrape函数中调用异步函数,那么您将拥有一个紧密的循环回调,其中node.js将中止抱怨堆栈太大。