Загрузка массива файлов XML с помощью асинхронного XMLHttpRequest - PullRequest
1 голос
/ 07 декабря 2010

Я хочу загрузить массив файлов XML и сохранить их в массиве.

Пример кода:

var src = [ "a", "b", "c", "d" ];
var dest = {};
for (var i in src) {
    var req = new XMLHttpRequest();
    req.open("GET", src[i], true);
    req.onreadystatechange = function(aEvt) {
        if (req.readyState == 4) {
            if (req.status == 200) {
                dump(i + "\n");
                dest[i] = req.responseXML;
            }
        }
    }
    req.send(null);
}

Однако, результат дампа всегда

3

3

3

3

Это показывает, что i, на который ссылается обратный вызов, всегда является внешним i, поэтому файлы XML не могут быть сохранены правильно.

Итак, как решить эту проблему? У нас есть около 50 файлов XML для загрузки, и загрузка их по одному недопустима.

Спасибо.

Ответы [ 2 ]

5 голосов
/ 07 декабря 2010
  1. Не используйте for..in для циклического перебора массивов. Используйте обычный цикл for.
  2. Вы делаете типичную ошибку при создании функции в цикле. JavaScript не имеет области видимости блока, только области видимости функции, поэтому i всегда будет ссылаться на последний элемент массива, который вы зациклили при выполнении созданных вами функций. Все они имеют ссылку на того же i. Вы можете решить эту проблему, используя немедленно выполняемую функцию, которая возвращает функцию (таким образом, захватывая значение i).
  3. Если вы сделаете это, вам также потребуется захватить ссылку на req, иначе она всегда будет ссылаться на последний сгенерированный запрос XMLHttpRequest (по той же причине, что и для i).

Таким образом, одним из решений будет:

var src = [ "a", "b", "c", "d" ];
var dest = {};
for (var i = src.length;i--;) {
    var req = new XMLHttpRequest();
    req.open("GET", xmlfile, true);
    req.onreadystatechange = (function(i, req) {
        return function(aEvt) {
            if (req.readyState == 4) {
                if (req.status == 200) {
                    dump(i + "\n");
                    dest[i] = req.responseXML;
                }
            }
        };
    }(i, req)); // capturing the current value/reference of i and req
    req.send(null);
}

@ Решения Spiny Norman могут быть более читабельными;)

3 голосов
/ 07 декабря 2010

Вы могли бы сделать что-то вроде этого:

var src = [ "a", "b", "c", "d" ];
var dest = {};
var loadXml = function(i) {
    var req = new XMLHttpRequest();
    req.open("GET", xmlfile, true);
    req.onreadystatechange = function(aEvt) {
        if (req.readyState == 4) {
            if (req.status == 200) {
                dump(i + "\n");
                dest[i] = req.responseXML;
            }
        }
    }
    req.send(null);
};

for (var x = 0; x < src.length; x++) {
    loadXml(x);
}

Кстати, кажется, вы всегда загружаете один и тот же xml-файл, но я уверен, что в вашем реальном коде это отличается;)

...