1

要点を説明します: 私はこのループを持っています:

for (var i = 1; i <= toSchedule; i++) {
        when = trackWrapper.lastPlay +
                (trackDuration +
                (looper.timeInterval - trackDuration));

        track.play(true, when);
        trackWrapper.lastPlay = when;
    }

playメソッドの本体には次のように記述されています。

[...]
// Here when is a different value for each call (verified)
// Many calls of the play method are performed before the returned function below is run as a callback
        function playingCallback(myWhen){

            return function(buffers){
                // Here myWhen will always be equal to the value of the last call occurred BEFORE the first callback execution
                console.log("myWhen = "+myWhen);
                [...]

            };
        };

        var realCallback = playingCallback(when);

        track.scheduled.push(when);
        track.recorder.getBuffer(realCallback);

たとえば、次のようになります。

play(true, 1);
play(true, 2);
play(true, 3);

// Wait for it...

myWhen = 3;
myWhen = 3;
myWhen = 3;

今:私はクロージャーについて読んだ、「悪名高いループの問題」について読んだ、ここStackOverflowで何十もの答えを読んだが、これを理解できなかった. コールバックでこの種の問題が発生するのは 2 回目なので、現時点では、何が起こっているのか完全には理解できていないと思います。

上記のコードのどこが間違っているのか教えてください。前もって感謝します。

4

1 に答える 1

1

一般に、次のルールを理解する必要があります。クロージャーは、スコープから出た後でも、その「周囲のスコープ」にアクセスできます。しかし、それは実行時のスコープの状態であり、クロージャの作成時ではありません(!)

ループ内にクロージャを作成すると、ループ変数にアクセスできます。しかし、ループはすでに終了している可能性が高いです。したがって、ループ変数は最後のループの値を保持します。

したがって、クロージャーがコールバックの場合は、作成時に関連するスコープ変数のコピーを作成し、実行時にこのコピーを使用する必要があります。これを行うには、(たとえば) すぐに実行される無名関数から内部クロージャーを作成します。

function myOuterScope(count) {
   for(i=0; i<count; i++) {
      setTimeout((function(local_i) {
         // this function will be immediately executed. local_i is a copy of i at creation time
         return function() {
            // this is the function that will be called as a callback to setTimeout
            // use local_i here, and it will be 0, 1, 2 instead of 3, 3, 3
         }
      })(i)
      , 1000);
   }
}
myOuterScope(3);
于 2013-09-26T13:25:50.503 に答える