2015-11-03 59 views
0

有人可以幫我理解我的解決方案爲什麼不起作用嗎?看起來回調函數在juggle函數完成之前正在運行。回撥首先運行? - learnyounode玩雜耍

我的代碼工作正常,如果我刪除評論。這只是我不明白爲什麼日誌功能沒有在玩轉功能結束後被調用。這就是回調應該如何正確工作?

感謝提前:)

var http = require('http') 
links = process.argv.slice(2) 
var contents = [] 
//var cbacks = 0 

function juggle(callback) { 
    links.forEach(function(link, i, links) { 
     http.get(link, function(response) { 
      response.setEncoding("utf8") 
      var str = "" 
      response.on("data", function(data) { 
       str = str.concat(data) 
      }) 
      response.on("end", function(){ 
       contents[i] = str 
       //cbacks++ 
       //if(cbacks === 3) { 
       // callback() 
       //} 
      }) 
     }) 
    }) 
    callback() 
} 

function log() { 
    contents.forEach(function(content, i, contents) { 
     console.log(contents[i]) 
    }) 
} 

juggle(log) 
+1

'callback'函數在'http.get'的回調之前運行 - 因爲'http.get'是一個異步調用。 – tymeJV

+0

啊,非常感謝!不敢相信我沒有想到這一點。 – tushar

回答

0

所以你有全球範圍,這是我會用來實際上處理請求。

當每個鏈接被註冊到EventEmitter時,您可以將其存儲在地圖中。

var link_map = {}; 
var request_counter = 0; 

links.forEach(function (link, index) { 
    link_map[link] = ''; 
... 

然後在您的要求,您可以從一個特定的請求追加數據

response.on('data', function (chunk) { 
    link_map[link] += chunk.toString(); 
... 

終於在每一端,檢查是否所有的請求已完成

response.on('end', function() { 
    request_counter += 1; 
    if (links.length === request_counter) { 
     // do your logging stuff here and you have all 
     // the data that you need inside link_map 
... 

link變量的內在forEach中聲明的匿名函數將被存儲用於該閉包。因此,每發生一次'data'事件,link變量將引用已註冊到回調的特定鏈接的請求。這就是爲什麼我選擇使用地圖數據結構並將特定數據映射到我們用作關鍵字的每個鏈接。

如果您不熟悉事件發送器和回調函數,可能會遇到一些困難。不斷練習,最終會變得更容易。

和你一樣使用一個數組並不是不正確的或者任何東西,我只是喜歡在鍵入時使用具有鍵值對的對象。

在您的機器上運行此代碼以查看它的行動。

const http = require('http'); 

var links = [ 
     'http://www.google.com', 
     'http://www.example.com', 
     'http://www.yahoo.com' 
]; 
var link_map = {}; 
var request_counter = 0; 

links.forEach(function (link, index) { 
    link_map[link] = ''; 

    http.get(link, function(response) { 
     response.on('data', function (chunk) { 
      link_map[link] += chunk.toString(); 
     }); 

     response.on('end', function() { 
      request_counter += 1; 
      if (links.length === request_counter) { 
       links.forEach(function(link) { 
        require('fs').writeFileSync(link.split('//')[1],link_map[link]); 
       }); 
      } 
     }); 
    }); 
}); 

您可以從父目錄中的鏈接看到文件的輸出。

+0

感謝您花時間來解釋!字典的確會非常有效。 – tushar

1

http.get是異步的。 forEach針對您的鏈接執行,該鏈接調用http.get,該鏈接註冊要處理的連接。它實際上並沒有完成連接/請求。

如果您需要在所有forEach函數完成時執行回調,則可以使用庫如async來完成回調。

async支持forEach方法。使用asyncforEach的第一個參數將採用額外的callback函數,應調用該函數以表示項目處理已完成。您可以將該回調放入response.on('end')回調中。當所有這些回調被調用時,或者發生錯誤時,async.forEach將執行您提供給它的onComplete回調作爲第三個參數,以實現您的目標。

+1

如果你真的想學習如何在Javascript中處理異步請求和函數,那麼我不會以異步開始。只是我的建議,你不必把它OP。 – nf071590

+0

感謝您的答案。我的確在努力避免使用異步並且學會拼命玩弄。 – tushar

0

您沒有立即等待http響應並調用回調函數。此時代碼contents數組爲空。

+0

在處理node/javascript中的回調和異步請求時,我不會使用術語wait。沒有什麼是真正的「等待」......事件被註冊並且事件被髮射......等等。它可能看起來像是一分鐘的細節,但語法很重要!畢竟我們是語言學家...... – nf071590