2015-11-03 48 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