异步请求后的数组中未定义的索引

时间:2011-12-24 06:22:00

标签: javascript jquery ajax json asynchronous

我在一个完全定义良好的数组(在异步调用之外)遇到一些困难但是当我在异步请求(例如$ .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);
        }
    }

});         

}

1 个答案:

答案 0 :(得分:4)

问题是回调函数在ajax函数完成后的某个时间发生。到那时,数组索引已经前进到for循环的末尾(因此它指向数组的末尾)到一个未定义的值。数组仍然存在,但是在调用完成函数时您的索引已经更改。

通常用于将索引引入成功处理程序的技术是在函数闭包中捕获它,捕获它以在完成函数中使用。

您可以通过用以下代码替换成功处理程序来创建一个捕获索引值的闭包:

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

此外部函数执行并创建一个闭包,该闭包捕获i的值并唯一使其可用于成功处理程序。当它自己执行时,它返回你的成功处理程序,因此传递给getJSON函数以便稍后调用。但是,当稍后调用它时,您需要的i的值可通过自执行函数中的参数提供给成功处理程序。

这是考虑与回调一起使用的闭包的另一种方法。

  1. 任何函数都可以访问声明范围内的所有变量,甚至可以访问父作用域中更高级别的变量,即使该函数稍后作为回调调用。这实际上是javascript的一个巨大功能,许多其他语言都没有。
  2. 因此,如果我们希望稍后在回调执行时变量可用于回调函数,我们只需要以某种方式将该变量放入该回调函数的范围内。
  3. 以下是一个例子:

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

    在这种情况下,函数next()是对setTimeout()的回调,但该函数可以完全访问其父作用域中的变量:selcntdelayobj

    1. 如果变量在最初设置回调和回调被调用之间没有变化,那么这很容易。您可以使用匿名函数声明,并且在稍后调用回调时,在定义匿名函数时可用的所有更高范围变量仍然可用。
    2. 如果变量确实在代码继续执行时发生变化,并且您希望在稍后调用它时创建一个现在可用于回调的特定值 - 这就是它变得有点棘手的时候。可以做的是创建一个将此变量传递给的函数,从而创建一个范围,其中该变量具有您想要的值以及在其他代码继续执行时它不会更改的位置。但是,因为我们想要的是回调函数,而不仅仅是函数调用,我们必须以半怪异的方式将两者结合起来。我们进行函数调用并将其传递给所需的值。在该函数调用中,我们返回对回调函数的引用。这会适当地分配回调函数,但它会将回调函数放入一个闭包中,该闭包捕获我们所需变量的值。更好的是,这个闭包对于这个回调的特定实例是唯一的,所以回调的每次使用都会有它自己的闭包,因此它是该变量的唯一值。我们为您的特定问题创建的闭包/回调就是一个例子。