素朴な解決策
少数のスクレイプに対して採用できる「素朴な」解決策は、すべてをネストすることです (最後のスクレイプのコールバックで各スクレイプを開始し、最後のコールバックには render メソッドが含まれます)。
scrape
cb: scrape
cb: scrape
cb: render all results
もちろん、それは退屈で判読不能になります。(そして、すべてが並列ではなく直列で実行されるため、それほど高速ではありません。)
より良い解決策
render
より良い解決策は、すべてが返されたときに返された結果と呼び出しの数をカウントする関数を作成することです。1 つの実装を次に示します。
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 });
});
});