天真的解决方案
对于少量刮擦,您可以采用的“幼稚”解决方案是嵌套所有内容(在最后一次刮擦的回调中开始每个刮擦,最后一次回调包含渲染方法。)
scrape
cb: scrape
cb: scrape
cb: render all results
当然,这变得乏味且难以辨认。(而且一切都是串联运行的,而不是并行运行的,这不会很快。)
更好的解决方案
更好的解决方案是编写一个函数来计算render
所有返回结果和调用的数量。这是一种实现:
function parallel_cb(total, finalCallback) {
var done = 0;
var results = [];
return function(result) {
done += 1;
results.push(result);
if (total == done) finalCallback(results);
}
}
要在您的示例中使用它:
app.get('/results', function(req, res) {
var myCallback = parallel_cb(
sitesToScrape.count, // or 3 in this case
function(items) {res.render('results', { items: items })});
jsdom.env(
"http://nodejs.org/dist/",
["http://code.jquery.com/jquery.js"],
function (errors, window) {
// do some scraping
myCallback(result_from_scrape);
}
);
jsdom.env(
"http://nodejs.org/dist/",
["http://code.jquery.com/jquery.js"],
function (errors, window) {
// more scraping
myCallback(result_from_scrape);
}
);
jsdom.env(
"http://nodejs.org/dist/",
["http://code.jquery.com/jquery.js"],
function (errors, window) {
// even more scraping
myCallback(result_from_scrape);
}
);
});
最佳解决方案
您应该真正学习使用现有的并行/异步库,而不是自己编写,正如@almypal 在对您的问题的评论中所建议的那样。
async
如文档中所述,您可以做一些更整洁的事情:https ://github.com/caolan/async#parallel
或者,如果您的所有抓取实际上都在结果页面中寻找相同的元素,您甚至可以对要抓取的 URL 数组进行并行映射:https ://github.com/caolan/async#maparr-iterator-callback
您的每个抓取都可以使用异步并行方法提供的回调函数来返回其抓取的结果。最后的 [可选] 回调将包含您对render
所有项目的调用。
编辑:你要求的例子
这是您的代码,直接翻译到async
库中:
var async = require("async");
app.get('/results', function(req, res) {
async.parallel( // the first argument is an array of functions
[
// this cb (callback) is what you use to let the async
// function know that you're done, and give it your result
function (cb) {
jsdom.env(
"http://nodejs.org/dist/",
["http://code.jquery.com/jquery.js"],
function (errors, window) {
// do some scraping
// async's callback expects an error for the first
// param and the result as the second param
cb(null, result_from_scrape); //No error
}
);
},
function (cb) {
jsdom.env(
"http://nodejs.org/dist/",
["http://code.jquery.com/jquery.js"],
function (errors, window) {
// more scraping
cb(null, result_from_scrape);
}
);
},
function (cb) {
jsdom.env(
"http://nodejs.org/dist/",
["http://code.jquery.com/jquery.js"],
function (errors, window) {
// even more scraping
cb(null, result_from_scrape);
}
);
}
],
// This is the "optional callback". We need it to render.
function (err, results) {
// If any of the parallel calls returned an error instead
// of null, it's now in the err variable.
if (err) res.render('error_template', {error: err});
else res.render('results', { items: results });
});
});