0

我需要一个包含 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 ,控制台会显示一个奇怪的数组,其中每个位置未定义,最后一个位置未完成。

4

2 回答 2

2

这是一个非常常见的错误。处理程序在您的循环完成onload后被长时间调用。for这意味着 的值i将是循环结束时的值,而不是您希望它从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();

}
于 2013-01-05T01:54:51.627 回答
1

所有 ajax 的回调都引用相同i的外部范围。这意味着当您的 ajax 调用完成时,它们都会将数据推送到相同itotal-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)

可以在此处找到对上述代码的更详细说明。

于 2013-01-05T01:53:53.597 回答