2012-11-04 14 views
34

Sono sicuro che il mio problema si basa su una mancanza di comprensione della programmazione asynch in node.js ma qui va.Node.JS: come passare variabili a callback asincroni?

Ad esempio: ho un elenco di collegamenti che desidero sottoporre a scansione. Quando viene restituita una richiesta asincrona, desidero sapere a quale URL è destinato. Ma, presumibilmente a causa delle condizioni di gara, ogni richiesta ritorna con l'URL impostato sull'ultimo valore nell'elenco.

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

uscita prevista:

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

uscita effettiva:

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

Quindi la mia domanda è o:

  1. Come faccio a passare url (in valore) al richiamare la funzione? OR
  2. Qual è il modo corretto di concatenare le richieste HTTP in modo che vengano eseguite in sequenza? OR
  3. Qualcos'altro che mi manca?

PS: Per 1. Non voglio una soluzione che esamina i parametri del callback ma un modo generale di un callback conoscendo le variabili 'dall'alto'.

risposta

46

La variabile url non ha ambito per il ciclo for poiché JavaScript supporta solo l'ambito globale e della funzione. Quindi è necessario creare un ambito funzione per la vostra request chiamata per acquisire il valore url in ogni iterazione del ciclo utilizzando una funzione immediata:

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

BTW, incorporando un require nel mezzo del ciclo non è buona pratica. Probabilmente dovrebbe essere riscritto come:

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]); 
} 
+3

Non si tratta di ambito. Si tratta di chiusure. Molte altre lingue non hanno ancora un ambito di blocco, ma non affrontano questo problema a causa della mancanza di chiusure. – slebetman

+3

Il punto è che se JavaScript supportava l'ambito del blocco, allora l'accesso di chiusura a 'url' nella funzione di callback del codice dell'OP funzionerebbe perché ogni iterazione del ciclo avrebbe la propria variabile' url' (come in C#). – JohnnyHK

+1

Il requisito è solo lì per brevità, nel mio codice tutto ciò che richiede è all'inizio. – Marc

8

Vedi https://stackoverflow.com/a/11747331/243639 per una discussione generale di questo problema.

Io suggerirei qualcosa come

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)); 
} 
+0

Ci sono molti link migliori da dare su StackOverflow per spiegare il problema. Questo per esempio: http://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops/3572616#3572616 – slebetman

8

Check this out blog. Una variabile può essere passata usando il metodo .bind(). Nel tuo caso sarebbe così:

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})); 
} 
+1

Mi piace questo metodo in quanto è molto meno prolisso di avvolgere qualcosa in una funzione. –

Problemi correlati