59

次のコードがあります。

for(var i = 0; i < list.length; i++){
    mc_cli.get(list[i], function(err, response) {
        do_something(i);
    });
}

mc_climemcached データベースへの接続です。ご想像のとおり、コールバック関数は非同期であるため、for ループが既に終了しているときに実行される可能性があります。また、この方法do_something(i)で呼び出すと、常に for ループの最後の値が使用されます。

この方法でクロージャーを試してみました

do_something((function(x){return x})(i)) 

しかし、どうやらこれも常に for ループのインデックスの最後の値を使用しているようです。

また、次のように for ループの前に関数を宣言しようとしました。

var create_closure = function(i) {
    return function() {
        return i;
    }
}

そして呼び出す

do_something(create_closure(i)())

しかし、再び成功せず、戻り値は常に for ループの最後の値になります。

クロージャーで何が間違っているのか誰か教えてもらえますか? 私はそれらを理解していると思っていましたが、なぜこれが機能していないのかわかりません。

4

10 に答える 10

81

配列を介して実行しているためforEach、リスト項目を提供する which と、コールバックでインデックスを使用するだけです。反復には独自のスコープがあります。

list.forEach(function(listItem, index){
  mc_cli.get(listItem, function(err, response) {
    do_something(index);
  });
});
于 2012-11-12T12:02:31.963 に答える
17

これは古いスレッドであることは知っていますが、とにかく私の答えを追加してください。ES2015letには、反復ごとにループ変数を再バインドする機能があるため、非同期コールバックでループ変数の値を維持するため、以下を試すことができます。

for(let i = 0; i < list.length; i++){
    mc_cli.get(list[i], function(err, response) {
        do_something(i);
    });
}

ただし、とにかく、ES2015 の機能であり、すべてのブラウザーと実装をサポートしていない可能性があるためforEach、 immediately-invoked-function を使用してクロージャーを使用または作成することをお勧めします。ここletから、 Edge 13まで、さらにはFirefox 49までサポートされていないことがわかります (これらのブラウザーは確認していません)。Node 4 ではサポートされていないとさえ書かれていますが、私が個人的にテストしたところ、サポートされているようです。Bindings ->let->for/for-in loop iteration scope

于 2016-07-23T14:23:47.100 に答える
14

あなたはかなり近づいていましたが、クロージャーをgetコールバック内に置くのではなく、に渡す必要があります。

function createCallback(i) {
    return function(){
        do_something(i);
    }
}


for(var i = 0; i < list.length; i++){
    mc_cli.get(list[i], createCallback(i));
}
于 2012-11-12T12:07:55.337 に答える