2014-10-01 48 views
0

我一直在learnyounode的幫助下學習nodejs,並且我被困在雜耍異步中。我的解決方案除了解決問題中最難的部分之外 - 它會按順序打印結果,因爲有些請求先於其他請求完成。我試圖在沒有使用任何幫助程序庫的情況下做到這一點,所以我打了谷歌。我找到了一個關於控制流的好教程 - http://book.mixu.net/node/ch7.html,但發現他的Series for a - for asynchronous for循環只能正常工作,因爲他的異步函數在返回之前等待1000毫秒。在我的例子中,時間是可變的,所以你不會以相同的順序得到結果。然後,這個例子進入構建控制流庫,我覺得這樣做會超出learnyounode任務的範圍。雜耍異步 - LearnYouNode - 立即調用函數表達式

有些事情我已經試過有:

製作包含URL,命令和響應數據的對象。我遇到的問題是,當我坐在http.get的回調中時,我不知道我獲取數據的URL是什麼。這實際上是我嘗試過的大多數事情的錯誤,我不知道如何將數據鏈接回到回調中的特定URL。

使用asyncCounter作爲索引,專門爲存儲陣列中的返回數據設置一個位置。所以當事件「結束」發生時,我會按照您在下面的代碼中看到的那樣進行設置。很明顯,這是行不通的,因爲通話在不同的時間完成。

最終我放下心來,看着解決方案。以下是我的所作所爲,並用星號標記了解決方案中缺少的內容。

var http = require("http"); 

var storage = []; 
var urlList = []; 
var asyncCounter = 0; 

//Store all the URL's from the command line in an array 
for(var i = 2; i < process.argv.length; i++){ 
    urlList.push(process.argv[i]); 
} 

//This function prints out all the data in the storage array 
//The storage array contains the data received from http.get 
function AsyncComplete(){ 
    for(var j = 0; j < storage.length; j++){ 
     console.log(storage[j]); 
    }; 
}; 

//Entry function 
function main(){ 
    //Do an http get on each of the provided URL's 
    for(var i = 0; i < urlList.length; i++){ 
     **(function(i){** 
      http.get(urlList[i], function(response){ 
       var body = ""; 
       //Got a chunk of data! 
       response.on("data", function(chunk){ 
        body += chunk; 
       }); 
       //All done with our request. 
       response.on("end", function(){ 
        //Store the total response from the URL 
        storage[**i**] = body; 
        asyncCounter++; 
        //Check if we can print the results yet 
        //I want to wait for all the calls to complete so I am using a counter 
        if(asyncCounter == urlList.length){ 
         AsyncComplete(); 
        } 
       }); 
      }); 
     **})(i);** 
    }; 
}; 

main(); 

所以我再次擊中谷歌,因爲我不知道他們在做什麼,我發現了一個叫做閉包的東西。根據我的理解,他們所做的一切使我保持在回調的範圍之內 - 我無法弄清楚該如何去做,所以認爲這是不可能的,並且很早就放棄了。

我的問題是,這是如何工作 - 特別是他們爲什麼有(一)他們在哪裏,他們的目的是什麼。如果沒有(i),解決方案將無法正常工作。爲什麼它只是拋出(功能(我){某處?這是我從未想過在一百萬年內做的事情

我發現https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures,這似乎可以包含答案,所以我經歷在此期間閱讀,看看我能拿出的東西。

預先感謝您的時間!

史蒂夫。

回答

2

這被稱爲立即調用此函數表達式(IIFE)只是一個立即被調用的匿名函數,傳遞給這個IIFE的參數是放在調用堆棧上,與for循環的外部i不同。你可以想象它像創建當前i的快照。

在用「var」聲明的JavaScript變量是函數作用域。所以,所有回調將訪問相同的我(這不是你想要的)。通過將它傳遞給IIFE(這是一個函數,從而爲變量i引入一個自己的範圍),爲每個回調創建一個新變量。 如果給IIFE的參數另一個名字,它會變得更加明顯。例如:

(function(savedI) { // ... })(i)

通過命名參數i,原i是陰影。這意味着你不能訪問IIFE內部的「外部」。

使用IIFE的常見替代方法是使用地圖代替。例如:

_.range(urlList.length).map(function(i) { // ... })

_.range是從underscore效用函數並給出了一個陣列到達從0直到urlList.length。傳遞的函數將收到一個參數i,它不會被覆蓋(就像for循環在你的版本中做的那樣)。

避免該問題的另一種可能性是通過聲明變量,讓這可能在EcmaScript 6或更高版本中。這引入了塊範圍的變量。你可以閱讀更多關於它here。例如:

for(let i = 0; i < urlList.length; i++){ // .. }

+0

非常感謝您的評論。完美地回答我的問題。我在看完全是錯誤的東西。 -.- – Steve 2014-10-02 13:30:59

相關問題