为什么onload处理程序无法正确填充我的数组?

时间:2013-01-05 01:47:37

标签: javascript html5 fileapi

我需要一个包含blob的数组,所以我的代码是:

for (var i = 0; i < total; i++) {

    var xhr = createXHR();

    xhr.open('GET', 'img/tiles/' + zeroFill(i, 4) + '.png', true);
    xhr.responseType = 'blob';

    xhr.onload = function() {

        arr[i] = new Blob([this.response], {type: 'image/png'});
        // console.log(arr[i]);

    };

    xhr.send();

}

当我输出 arr i 位置时,控制台会正确显示blob(至少说明它的大小)。如果我尝试显示以前的位置,我会 undefined

如果我在完成所有XHR请求后查看 arr ,控制台会显示一个奇怪的数组,每个位置未定义,最后一个带有未完成的blob。

2 个答案:

答案 0 :(得分:2)

这是一个非常常见的错误。在onload循环完成后,for处理程序会被调用很长时间。这意味着i的值将是循环END处的值,而不是您希望它来自for循环中间的值。

要修复它,您需要以某种形式在闭包中捕获i的正确值。有很多方法可以做到这一点。

这是一种使用自执行函数的方法,该函数捕获函数参数中i的值。 i的值传递给自执行函数,该函数为for循环的每次迭代创建一个新范围,然后在该范围的函数参数中捕获i的正确值。该函数参数对于每次对自执行函数的调用都是唯一存在的,因此在将来某个时间调用onload处理程序时需要保留所需的值。这是看起来的样子:

for (var i = 0; i < total; i++) {

    var xhr = createXHR();

    xhr.open('GET', 'img/tiles/' + zeroFill(i, 4) + '.png', true);
    xhr.responseType = 'blob';

    (function(index) {
        xhr.onload = function() {

            arr[index] = new Blob([this.response], {type: 'image/png'});
            // console.log(arr[index]);
       }

    })(i);

    xhr.send();

}

答案 1 :(得分:1)

所有ajax的回调都引用了外部作用域的i。这意味着当您的ajax调用完成后,他们都会将数据推送到i total-1 {。}}。

旁注:以前的索引填充了null,这正是将数据推送到更大索引时JS数组的工作方式。

一个常见的解决方案是使用闭包,将当前i值捕获到新的执行上下文中:

//read comments in the numeric order
xhr.onload = (function(i) {//2. the `i` inside the function now references
                           //a new `i` independent from the outer `i`
    return function(){//3. returns a function handler that executes on xhr.onload
        arr[i] = new Blob([this.response], {type: 'image/png'});
        console.log(i); //4. logs the IIFE execution context's `i`,
                        //as it is the closest (inner-most scope chain-wise) `i`
    };
}(i)); //1. Passes outer current `i` as argument to this
       //Immediately-Invoked Function Expression (IIFE)

可以找到上述代码的更详细说明here