46

我确定我的问题是基于对 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. 我不想要一个检查回调参数的解决方案,而是一种通用的回调方法,它知道“从上面”的变量。

4

3 回答 3

49

您的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]);
}

顺便说一句,在循环中间嵌入 arequire不是好的做法。它可能应该重写为:

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]);
}
于 2012-11-04T19:11:16.993 回答
13

看看这个博客。可以使用 .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}));
}
于 2016-05-01T20:50:19.963 回答
7

有关此问题的一般讨论,请参阅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));
}
于 2012-11-04T19:15:43.977 回答