Índices no definidos en la matriz después de solicitudes asíncronas

Tengo algunas dificultades con una matriz que está (fuera de una llamada asíncrona) perfectamente bien definida, pero cuando invoco sus índices dentro de una solicitud asíncrona (por ejemplo, $ .getJSON) todos los índices de la matriz no están definidos pero la longitud es Siempre lo mismo. Aquí está mi código.

La matriz a la que me refiero es friendsArray.

La matriz tiene el índice correcto durante la segunda llamada “$ .getJSON” pero dentro de la callback de esa función, todos sus índices se vuelven indefinidos. ¿No debería la matriz mantener sus valores desde que se definió dentro del scope del método?

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); } } }); 

}

El problema es que la función de callback ocurre algún tiempo después cuando la función ajax se completa. Para entonces, el índice de la matriz ha avanzado al final de su bucle for (por lo tanto, apunta al final de la matriz) a un valor indefinido. La matriz aún está allí, pero su índice ha sido cambiado cuando se llama a la función de finalización.

La técnica que normalmente se usa para obtener el índice en el controlador de éxito es capturarlo en un cierre de función donde se captura para usarlo en la función de finalización.

Puede crear un cierre que capture el valor del índice reemplazando su controlador de éxito con esto:

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

Esta función externa se ejecuta y crea un cierre que captura el valor de i y lo hace único para el controlador de éxito. Cuando se ejecuta automáticamente, devuelve el controlador de éxito que, por lo tanto, se pasa a la función getJSON para que se llame más tarde. Pero, cuando se llama más tarde, el valor de i que necesita está disponible para el controlador de éxito a través del argumento en la función de auto ejecución.

Aquí hay otra forma de pensar acerca de los cierres utilizados con devoluciones de llamada.

  1. Cualquier función tiene acceso a todas las variables que están dentro del scope cuando se declara, incluso las variables que están en un nivel más alto en los ámbitos primarios e incluso si la función se llama más tarde como una callback. Esta es realmente una gran característica de javascript que muchos otros idiomas no tienen.
  2. Por lo tanto, si queremos que una variable esté disponible para una función de callback más adelante cuando se ejecute la callback, solo necesitamos que esa variable entre en el scope de esa función de callback.

Aquí hay un ejemplo de eso:

 function cycleOnOff(sel, cnt, delay) { var obj = $(sel); function next() { if (cnt-- > 0) { obj.toggle(); setTimeout(next, delay); } } next(); } 

En este caso, la función next() es una callback a setTimeout() , pero esa función tiene acceso completo a las variables dentro de su ámbito principal: sel , cnt , delay y obj .

  1. Si la variable no cambia entre el momento en que se establece inicialmente la callback y cuando se llama a la callback, entonces es bastante fácil. Solo puede usar una statement de función anónima y todas las variables de mayor scope disponibles en el momento de definir la función anónima seguirán estando disponibles cuando se llame a la callback más tarde.
  2. Si la variable cambia a medida que el código continúa ejecutándose y desea hacer que un valor específico que ahora está disponible para la callback cuando se llame más tarde, es cuando se vuelve un poco más complicado. Lo que uno puede hacer es crear una función a la que le pase esta variable, creando así un ámbito en el que esa variable tenga el valor que desea y donde no cambie a medida que el otro código continúe ejecutándose. Pero, porque lo que queremos es una función de callback, no solo una llamada de función, tenemos que combinar las dos de una manera casi extraña. Hacemos una llamada de función y le pasamos el valor deseado. En esa llamada de función, devolvemos una referencia a nuestra función de callback. Esto asigna la función de callback adecuadamente, pero coloca la función de callback en un cierre que captura el valor de nuestra variable deseada. Aún mejor, este cierre es exclusivo de esta instancia particular de la callback, por lo que cada uso de la callback tendrá su propio cierre y, por lo tanto, su propio valor único de esa variable. El cierre / callback que creamos para su problema particular es un ejemplo de esto.