2012-11-04 9 views
34

Jestem pewien, że mój problem jest oparty na braku zrozumienia programowania asynch w node.js, ale tutaj jest.Node.JS: Jak przekazywać zmienne do asynchronicznych wywołań zwrotnych?

Na przykład: Mam listę linków, które chcę indeksować. Kiedy każde żądanie asynchroniczne się zwraca, chcę wiedzieć, do którego adresu URL służy. Jednak, prawdopodobnie ze względu na warunki wyścigu, każde żądanie powraca z adresem URL ustawionym na ostatnią wartość na liście.

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

oczekiwany wynik:

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

Rzeczywista moc:

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

Więc moje pytanie jest:

  1. Jak mogę przekazać URL (w ujęciu wartościowym) do funkcja oddzwaniania? LUB
  2. Jaki jest właściwy sposób łączenia żądań HTTP, aby były uruchamiane sekwencyjnie? LUB
  3. Coś jeszcze brakuje?

PS: Dla 1. Nie chcę rozwiązania, które bada parametry wywołania zwrotnego, ale ogólny sposób wywołania zwrotnego, wiedząc o zmiennych "z góry".

Odpowiedz

46

Twoja zmienna url nie jest ograniczona do pętli for, ponieważ JavaScript obsługuje tylko zakres globalny i zakres funkcji. Więc trzeba stworzyć zakres funkcji do wywołania request uchwycić wartość w każdej iteracji pętli url za pomocą natychmiastową funkcję:

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

BTW, osadzanie require w środku pętli nie jest dobra ćwiczyć. To prawdopodobnie powinien być ponownie zapisać jako:

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

Tu nie chodzi o zakres. Chodzi o zamknięcia. Wiele innych języków nie ma zasięgu blokowego, ale nie napotyka tego problemu z powodu braku zamknięć. – slebetman

+3

Chodzi o to, że jeśli JavaScript wspierał zakres blokowy, to dostęp do zamknięcia 'url' w funkcji wywołania zwrotnego kodu OP działałby, ponieważ każda iteracja pętli otrzymywałaby własną zmienną url (jak w C#). – JohnnyHK

+1

Wymaganie jest tam dla zwięzłości, w moim kodeksie wszystkie wymagania są na początku. – Marc

8

Zobacz https://stackoverflow.com/a/11747331/243639 do ogólnej dyskusji na ten temat.

sugeruję coś takiego

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

Istnieje wiele lepszych linków do podania w StackOverflow w celu wyjaśnienia problemu. Ten na przykład: http://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops/3572616#3572616 – slebetman

8

Check to blog zewnątrz. Zmienna może zostać przekazana przy użyciu metody .bind(). W twoim przypadku byłoby tak:

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

Bardziej lubię tę metodę, ponieważ jest o wiele mniej gadulska niż zawijanie czegoś w funkcji. –