JQuery异步承诺嵌套循环

时间:2014-09-03 03:49:00

标签: jquery node.js promise jquery-deferred q

我是Node.js和promises的新手(在这里,我正在使用Q.js)。 我正在尝试从具有以下结构的站点中制作一个刮板:

  • main_page :有一个类别列表,每个类别都有一个指向商店列表页面的链接。
  • 商店列表页面:有一个商店列表,每个商店都有一个指向商店详细信息页面的链接。
  • 商店详细信息页面:拥有我正在寻找的数据。

我做了第一个没有承诺的工作方法,但结果代码非常难看。所以我认为这是使用promises的好例子。

我无法使用这种方法。第二个循环完成后,应用程序不会继续(它永远不会执行 end()方法)。此外,我不知道如何附加第三个循环。

我怎么能这样做?

function get(url) {
    var deferred = Q.defer();
    requestify.get(url).then(function(response) {
        deferred.resolve(cheerio.load(response.getBody()));
    });
    return deferred.promise;
}

function process_main_page($) {
    var promises = [];
    $('.categories a').each(function(i) {
        var deferred = Q.defer();
        var storesList = $('.store');
        get($(this).attr('href')).then(function($) {
            deferred.resolve(process_stores_list(storesList));
        });
        promises.push(deferred);
    });
    return Q.all(promises);
}

function process_stores_list(storesList) {
    var promises = [];
    storesList.each(function() {

        // Here I need to make another ajax call for each store detail page, which has the data that I need.

        promises.push(deferred);
    });
    return Q.all(promises);
}

function end(res) {
    var deferred = Q.defer();
    fs.writeFile('output.json', JSON.stringify(myGatheredData, null, 4), function(err) {
        deferred.resolve(function() {
            res.send('File successfully written! - Check your project directory for the output.json file');
        });
    });
    return deferred.promise;
}

app.get('/', function(req, res) {
    get(url).then(process_main_page).then(end);
});

1 个答案:

答案 0 :(得分:2)

正如@BenjaminGruenbaum已经评论过,你的代码充斥着deferred antipatternQ.defer()的唯一(或多或少)合法使用是fs.writeFile,但您忘记在那里处理错误。 promisify that API更容易。

  

我无法使用这种方法。

整体结构似乎很好。但是,有些观点:

  • 您似乎永远不会从包含商店的页面中获取stores_list。您获取该页面,但是从类别页面解析了var storesList = $('.store');的承诺?
  • 您的end方法确实获得了myGatheredData - Q.all加入的结果数组 - 作为参数传递。它无法访问res ponse对象。
  

当第二个循环完成后,应用程序不会继续(它永远不会执行end()方法)。此外,我不知道如何附加第三个循环。

我认为原因是 - 您可能已经构建了Q.all()数组的延迟,但从未解决过它们。这使得返回的承诺"挂起" (保持待定状态),并且从未调用过end回调。

var write = Q.nbind(fs.writeFile, fs);
function get(url) {
    return requestify.get(url).then(function(response) {
        return cheerio.load(response.getBody()));
    });
}

function process_main_page($_main) {
    var promises = $_main('.categories a').map(function(i) {
        // var storesList = $_main('.store'); // not sure what this did
        return get($_main(this).attr('href')).then(process_storelist_page);
    }).toArray();
    return Q.all(promises);
}
function process_storelist_page($_stores) {
    return process_stores_list($_stores('a.store').map(function() {
        return $_stores(this).attr('href'); // whatever?
    }).toArray());
}

function process_stores_list(storesList) {
    var promises = $.map(storesList, function(store_url) {
        // Here make another ajax call for each store detail page
        return get(store_url).then(process_store_page););
    });
    return Q.all(promises);
}
function process_store_page($_store) { // which has the data that I need.
    return /* select some data from the page */;
}
function save_data(myGatheredData) {
    return write('output.json', JSON.stringify(myGatheredData, null, 4)).then(function() {
        return 'File successfully written! - Check your project directory for the output.json file';
        });
    });
}

app.get('/', function(req, res) {
    get(url).then(process_main_page).then(save_data).then(function end(result) {
        res.send(result);
    });
});

当然你也可以用函数表达式而不是我用过的函数声明来嵌套所有东西。