2012-11-04 85 views
34

我確定我的問題是基於對node.js中的異步編程缺乏理解,但是在這裏。Node.JS:如何將變量傳遞給異步回調?

例如:我有一個我想要抓取的鏈接列表。當每個異步請求返回時,我想知道它適用於哪個URL。但是,可能是因爲競爭條件,每個請求都會返回,並將URL設置爲列表中的最後一個值。

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

預期輸出:

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

實際輸出:

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

所以我的問題可以是:

  1. 我如何通過URL(按價值計算)的回撥功能?或
  2. 鏈接HTTP請求的正確方式是什麼,以便它們按順序運行?或
  3. 我失蹤的東西?

PS:對於1.我不想要一個解決方案來檢查回調的參數,但是知道變量'從上面'的回調的一般方法。

回答

46

您的url變量沒有作用於for循環,因爲JavaScript只支持全局和函數範圍。所以,你需要爲你的request調用創建一個函數範圍內使用即時功能,以捕捉每次循環的url值:

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

BTW,嵌入循環的中間require不好實踐。它可能應該重寫爲:

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

這不是範圍。這是關閉。很多其他語言沒有封鎖範圍,但由於缺乏封鎖而沒有面對這個問題。 – slebetman

+3

問題是,如果JavaScript確實支持塊範圍,那麼在OP代碼的回調函數中對'url'的閉包訪問就可以工作,因爲循環的每次迭代都會得到自己的url變量(就像在C#中一樣)。 – JohnnyHK

+1

爲了簡潔起見,需求就在那裏,在我的代碼中,所有需求都是在開始階段。 – Marc

8

有關此問題的一般性討論,請參見https://stackoverflow.com/a/11747331/243639

我建議像

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

在StackOverflow上有很多更好的鏈接來解釋這個問題。這一例如:http://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops/3572616#3572616 – slebetman

8

入住這blog出來。一個變量可以通過使用.bind()方法傳遞。在你的情況下,它會是這樣的:

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

我更喜歡這種方法,因爲它比詳細得多將某些東西包裝在一個函數中。 –

相關問題