web-dev-qa-db-fra.com

Requête HTTP parallèle async

Je rencontre un problème de flux de contrôle lorsqu'une application charge un grand nombre d'URL. J'utilise Caolan Async et le module de requête NPM.

Mon problème est que la réponse HTTP commence dès que la fonction est ajoutée à la file d'attente. Idéalement, je veux créer ma file d'attente et ne commencer à faire les demandes HTTP qu'au début de la file d'attente. Sinon, les callbacks commencent à se déclencher avant la file d'attente, ce qui entraîne sa fermeture prématurée.

var request = require('request') // https://www.npmjs.com/package/request
    , async = require('async'); // https://www.npmjs.com/package/async

var myLoaderQueue = []; // passed to async.parallel
var myUrls = ['http://...', 'http://...', 'http://...'] // 1000+ urls here

for(var i = 0; i < myUrls.length; i++){
    myLoaderQueue.Push(function(callback){

        // Async http request
        request(myUrls[i], function(error, response, html) {

            // Some processing is happening here before the callback is invoked
            callback(error, html);
        });
    });
}

// The loader queue has been made, now start to process the queue
async.parallel(queue, function(err, results){
    // Done
});

Y a-t-il un meilleur moyen d'attaquer cela?

11
ChrisRich

L'utilisation de boucles for combinées à des appels asynchrones est problématique (avec ES5) et peut générer des résultats inattendus (dans votre cas, l'URL erronée est extraite).

Au lieu de cela, envisagez d'utiliser async.map() :

async.map(myUrls, function(url, callback) {
  request(url, function(error, response, html) {
    // Some processing is happening here before the callback is invoked
    callback(error, html);
  });
}, function(err, results) {
  ...
});

Étant donné que vous avez plus de 1000 URL à récupérer, async.mapLimit() peut également être utile.

24
robertklep

Si vous êtes prêt à commencer à utiliser Bluebird et Babel pour utiliser promises et ES7async/await, vous pouvez procéder comme suit:

let Promise = require('bluebird');
let request = Promise.promisify(require('request'));

let myUrls = ['http://...', 'http://...', 'http://...'] // 1000+ urls here

async function load() {
  try {
    // map myUrls array into array of request promises
    // wait until all request promises in the array resolve
    let results = await Promise.all(myUrls.map(request));
    // don't know if Babel await supports syntax below
    // let results = await* myUrls.map(request));
    // print array of results or use forEach 
    // to process / collect them in any other way
    console.log(results)
  } catch (e) {
    console.log(e);
  }
}
7
krl

Je suis assez confiant que vous ressentez les résultats d'une erreur différente. Au moment où vos fonctions en file d'attente évaluent, j’ai été redéfini, ce qui peut donner l’apparence que vous avez manquée les premières URL. Essayez une petite fermeture lorsque vous modifiez les fonctions.

var request = require('request') // https://www.npmjs.com/package/request
    , async = require('async'); // https://www.npmjs.com/package/async

var myLoaderQueue = []; // passed to async.parallel
var myUrls = ['http://...', 'http://...', 'http://...'] // 1000+ urls here

for(var i = 0; i < myUrls.length; i++){
    (function(URLIndex){
       myLoaderQueue.Push(function(callback){

           // Async http request
           request(myUrls[URLIndex], function(error, response, html) {

               // Some processing is happening here before the callback is invoked
               callback(error, html);
           });
       });
    })(i);
}

// The loader queue has been made, now start to process the queue
async.parallel(queue, function(err, results){
    // Done
});
0
trex005