我是Node.js和promise的新手(在这里,我使用的是Q.js)。我正试图从一个具有以下结构的网站上制作一个刮刀:
- main_page:有一个类别列表,每个类别都有一个指向商店列表页面的链接
- 商店列表页面:有一个商店列表,每个商店都有一个指向商店详细信息页面的链接
- 商店详细信息页面:包含我正在查找的数据
我做了第一个没有承诺的工作方法,但结果代码非常难看。因此,我认为这是一个使用承诺的好例子。
我不能用这种方法工作。当第二个循环完成时,应用程序不会继续(它从不执行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);
});
正如@BenjaminGruenbaum已经评论的那样,您的代码中充斥着延迟的反模式。Q.defer()
的唯一(或多或少)合法使用是用于fs.writeFile
,但您忘记处理那里的错误。只承诺API比较容易。
我不能用这种方法工作。
整体结构看起来不错。然而,有些要点:
- 您似乎从未从具有存储的页面中获取
stores_list
。您获取了该页面,但使用类别页面中的var storesList = $('.store');
解析了promise end
方法确实获得了作为其参数传递的myGatheredData
(Q.all
连接的结果数组)。它没有对res
响应对象的任何访问权限
当第二个循环完成时,应用程序不会继续(它从不执行end()方法)。此外,我不知道如何连接第三个环。
我认为这就是原因——您可能已经在为Q.all()
数组构建延迟,但从未解决它们。这使得返回的promise"挂起"(保持挂起),并且从未调用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);
});
});
当然,您也可以用函数表达式嵌套所有内容,而不是我使用的函数声明。