2012-11-04 51 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

我更喜欢这种方法,因为它比详细得多将某些东西包装在一个函数中。 –

相关问题