2011-12-24 16 views
3

(非同期呼び出しの外で)完全に明確に定義されている配列では問題がありますが、インデックスをasynchリクエスト(例えば$ .getJSON)内で呼び出すと、配列の添字は未定義ですが、の長さはです。ここに私のコードです。非同期リクエスト後の配列内の未定義インデックス

私が参照しています配列があるfriendsArray

配列は、第二の「$ .getJSON」通話中に正しいインデックスを持っていますが、その関数のコールバック内のすべてのインデックスが不定となります。メソッドのスコープ内で定義されているので、配列はその値を保持するべきではありませんか?

if (response.status === 'connected') { 
var accessToken = $.trim(response.authResponse.accessToken); 
var hashArray = []; 
var friendArray = []; 
document.getElementById("statusCheck").innerHTML = accessToken; 
$.getJSON('https://graph.facebook.com/me/friends?access_token=' + accessToken, 
    function(dataJSON){ 
    hashArray = dataJSON['data']; 
    for (var i = 0; i < hashArray.length; i++){ 
     friendArray.push(hashArray[i]["id"]); 
    } 
    var resultJSON = "{"; 
    var resultArray = []; 

    for (var i = 0; i < friendArray.length; i++){ 
     $.getJSON('https://graph.facebook.com/me/mutualfriends/' + 
        friendArray[i] + "?access_token=" + accessToken, 
      function(dataJSON2){ 
       resultArray = dataJSON2['data']; 
       resultJSON += friendArray[i] + ":" + resultArray.length + ","; 
       //alert(resultJSON); 
      }) 
     if (i == friendArray.length - 1){ 
      postArrayPopulation(resultJSON); 
     } 
    } 

});   

}

答えて

4

問題は、コールバック関数がいくつかの時間後のAJAX機能が完了したときに起こることです。これまでに、配列インデックスはループの最後まで進んでいたため(配列の終わりを指す)、未定義の値になりました。配列はまだそこにありますが、インデックスは完了関数が呼び出されるまでに変更されています。

インデックスを成功ハンドラに取り込むために通常使用される手法は、完了関数で使用するためにキャプチャされる関数クロージャでキャプチャすることです。

あなたはこれであなたの成功ハンドラを置き換えることにより、インデックス値を取り込み、クロージャを作成することができます。

(function(index) { 
    return function(dataJSON2) { 
     resultArray = dataJSON2['data']; 
     resultJSON += friendArray[index] + ":" + resultArray.length + ","; 
     //alert(resultJSON); 
    } 
}) (i); 

この外側の関数が実行され、iの値をキャプチャクロージャを作成し、一意にそれが利用できるようにします成功ハンドラ。自己実行すると成功ハンドラが返され、getJSON関数に渡されて後で呼び出されます。しかし、後で呼び出されるときには、自分が必要とする値iが、自己実行関数の引数を介して成功ハンドラに渡されます。

ここでは、コールバックで使用されるクロージャについて考えてみましょう。

  1. 任意の関数は、それが宣言されていたときに範囲内にあるすべての変数へのアクセス権を持つ親スコープの上位レベルでアップしていても、変数や関数は、後にコールバックとして呼び出された場合でも。これは実際には他の多くの言語にはないjavascriptの巨大な機能です。
  2. したがって、コールバックが実行されるときにコールバック関数で変数を使用できるようにするには、その変数をそのコールバック関数のスコープに何らかの形で取得する必要があります。

は、ここでその例を示します。この場合

function cycleOnOff(sel, cnt, delay) { 
    var obj = $(sel); 

    function next() { 
     if (cnt-- > 0) { 
      obj.toggle(); 
      setTimeout(next, delay); 
     } 
    } 
    next(); 
} 

、機能next()setTimeout()へのコールバックですが、その機能は、その親スコープ内の変数へのフルアクセス権を持っている:selcntdelayおよびobj

  1. コールバックが最初に設定されてからコールバックが呼び出されるまでに変数が変更されない場合は、かなり簡単です。匿名関数宣言を使用するだけで、無名関数の定義時に使用可能なすべての上位スコープ変数は、後でコールバックが呼び出されたときでも利用可能になります。
  2. 変数は、コードの実行を継続し、それが後で呼び出されたとき、それは今コールバックに利用できる持っていることを特定の値にしたいように変更した場合 - それは少しトリッキー取得するときに、これはです。できることは、この変数を渡す関数を作成して、その変数が必要な値を持つスコープを作成し、他のコードが引き続き実行されても変更されない場所を作成することです。しかし、私たちが望むのは、関数呼び出しだけでなく、コールバック関数なので、2つを半奇妙な方法で結合する必要があります。関数呼び出しを行い、目的の値を渡します。その関数呼び出しでは、コールバック関数への参照を返します。これはコールバック関数を適切に割り当てますが、コールバック関数を目的の変数の値をキャプチャするクロージャに置きます。さらに、このクロージャはコールバックのこの特定のインスタンスに固有のものであるため、コールバックを使用するたびに独自のクロージャが使用されるため、その変数の独自の値が得られます。特定の問題のために作成したクロージャ/コールバックは、この例です。
+1

うわー、それは単に天才でした。私はそれを考えなかっただろう。 – Vivek

+0

だから、私は通常のコールバックの代わりにそのクロージャコードを置き換えて、それはうまくいくでしょうか?私は、特にコールバックと組み合わせて使用​​する場合、クロージャの周りを頭でつかむのに苦労しています。 – Vivek

+0

@Vivek - http://jsfiddle.net/jfriend00/BGkZh/で見て遊ぶ例です。私は私の答えにも説明を加えました。 – jfriend00

関連する問題