web-dev-qa-db-fra.com

Node.JS: Comment passer des variables aux rappels asynchrones?

Je suis sûr que mon problème est basé sur un manque de compréhension de la programmation asynchrone dans node.js, mais voilà.

Par exemple: J'ai une liste de liens que je veux explorer. Lorsque chaque demande asynchrone revient, je veux savoir à quelle URL elle est destinée. Mais, probablement en raison des conditions de concurrence, chaque demande revient avec l'URL définie sur la dernière valeur de la liste.

var links = ['http://google.com', 'http://yahoo.com'];
for (link in links) {
    var url = links[link];
    require('request')(url, function() {
        console.log(url);
    });
}

Production attendue:

http://google.com
http://yahoo.com

Sortie réelle:

http://yahoo.com
http://yahoo.com

Ma question est donc soit:

  1. Comment passer l'url (par valeur) à la fonction de rappel? OU
  2. Quelle est la bonne façon de chaîner les requêtes HTTP pour qu'elles s'exécutent séquentiellement? OU
  3. Quelque chose d'autre qui me manque?

PS: Pour 1. Je ne veux pas d'une solution qui examine les paramètres du rappel, mais une façon générale de rappeler les variables "d'en haut".

40
Marc

Votre variable url n'est pas étendue à la boucle for car JavaScript ne prend en charge que l'étendue globale et la fonction. Vous devez donc créer une étendue de fonction pour votre appel request pour capturer la valeur url à chaque itération de la boucle en utilisant une fonction immédiate:

var links = ['http://google.com', 'http://yahoo.com'];
for (link in links) {
    (function(url) {
        require('request')(url, function() {
            console.log(url);
        });
    })(links[link]);
}

BTW, incorporer un require au milieu de la boucle n'est pas une bonne pratique. Il devrait probablement être réécrit comme suit:

var request = require('request');
var links = ['http://google.com', 'http://yahoo.com'];
for (link in links) {
    (function(url) {
        request(url, function() {
            console.log(url);
        });
    })(links[link]);
}
49
JohnnyHK

Vérifiez cela blog out. Une variable peut être passée en utilisant la méthode .bind (). Dans votre cas, ce serait comme ceci:

var links = ['http://google.com', 'http://yahoo.com'];
for (link in links) {
var url = links[link];

require('request')(url, function() {

    console.log(this.urlAsy);

}.bind({urlAsy:url}));
}
9
Tjs

Voir https://stackoverflow.com/a/11747331/243639 pour une discussion générale sur ce problème.

Je suggérerais quelque chose comme

var links = ['http://google.com', 'http://yahoo.com'];

function createCallback(_url) {
    return function() {
        console.log(_url);
    }
};

for (link in links) {
    var url = links[link];
    require('request')(url, createCallback(url));
}
8
BobS