web-dev-qa-db-ja.com

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。コールバックのパラメーターを調べるソリューションではなく、「上から」の変数について知っているコールバックの一般的な方法が必要です。

40
Marc

JavaScriptはグローバルスコープと関数スコープのみをサポートするため、url変数のスコープはforループに限定されません。そのため、イミディエイト関数を使用して、ループの各反復で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]);
}

ところで、ループの途中に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]);
}
49
JohnnyHK

これを確認してください blog out。変数は、.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}));
}
9
Tjs

この問題の一般的な説明については、 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));
}
8
BobS