Ответ 1
Хорошо, я проверил ваш код и сделал несколько наблюдений.
-
Если вы точно не знаете, что делаете (или делаете это, чтобы узнать о XHR), я бы не использовал
XMLHttpRequest
напрямую. Существует несколько кросс-браузеров с этим объектом, поэтому я бы использовал библиотеку JS, чтобы помочь, jQuery является одним из наиболее очевидных. -
Я бы также попытался использовать подход с отсрочкой/обещанием, а не обратный вызов. Опять же, библиотека поможет вам здесь. jQuery имеет эти параметры для ajax, как и другие библиотеки, такие как when.js.
Таким образом, здесь некоторый код jsfiddle, на который вы можете посмотреть, что может дать вам некоторые идеи.
Во-первых, в новом модуле есть абхакция ajax, которая использует when.js отложенные. Модуль будет разрешать или отклонять отложенные в зависимости от успешности XHR или нет. Заметьте, что я остался в прямом использовании XHR, но я бы рекомендовал использовать библиотеку JS вместо этого.
// --------------------------------------------------
// New module
// Using https://github.com/cujojs/when/wiki/Examples
// --------------------------------------------------
define("my-ajax", ["when"], function (when) {
// TODO - Fake id only for testing
var id = 0;
function makeFakeJSFiddleRequestData(fileName) {
var data = {
fileName: fileName
};
data = "json=" + encodeURI(JSON.stringify(data));
var delayInSeconds = Math.floor(8 * Math.random());
data += "&delay=" + delayInSeconds;
return data;
}
return function loadJSON(args) {
// Create the deferred response
var deferred = when.defer();
var xhr = new XMLHttpRequest();
xhr.overrideMimeType("application/json");
var url = args.fileName;
// TODO - Override URL only for testing
url = "/echo/json/";
// TODO - Provide request data and timings only for testing
var start = +new Date();
var data = makeFakeJSFiddleRequestData(args.fileName);
// TODO - POST for testing. jsfiddle expects POST.
xhr.open("POST", url, true);
xhr.onreadystatechange = function () {
// TODO - duration only for testing.
var duration = +new Date() - start + "ms";
if (xhr.readyState == 4) {
if (xhr.status === 200) {
// Check that response is valid JSON
var json;
try {
json = JSON.parse(xhr.responseText);
} catch (e) {
console.log("rejected", args, duration);
deferred.reject(e);
return;
}
console.log("resolved", args, duration);
// TODO - Fake id only for testing
json.id = ("id" + id++);
deferred.resolve(json);
} else {
console.log("rejected", args, duration);
deferred.reject([xhr.status, args.fileName]);
}
}
}
// TODO - Provide request data only for testing
xhr.send(data);
// return the deferred promise.
// This promise will only be resolved or rejected when the XHR is complete.
return deferred.promise;
};
});
Теперь ваш модуль атласа выглядит примерно так (код изображения удален для ясности):
// --------------------------------------------------
// Your module
// Image stuff removed for clarity.
// --------------------------------------------------
define("tile-atlas", ["my-ajax", "when"], function (myAjax, when) {
var tilesheetPaths = [
"tilesheets/ground.json",
"tilesheets/ground-collision.json",
"tilesheets/objects-collision.json"];
// TODO - Changed to {} for Object keys
var tileAtlas = {};
function loadAtlasJSON() {
var deferreds = [];
// Save up all the AJAX calls as deferreds
for (var i = 0; i < tilesheetPaths.length; i++) {
deferreds.push(myAjax({
fileName: tilesheetPaths[i]
}));
}
// Return a new deferred that only resolves
// when all the ajax requests have come back.
return when.all(deferreds);
};
function addToTileAtlas(atlas) {
console.log("addToTileAtlas", atlas);
tileAtlas[atlas.id] = atlas;
}
function tileAtlasesReady() {
console.log("tileAtlasesReady", arguments);
var ajaxResponses = arguments[0];
for (var i = 0; i < ajaxResponses.length; i++) {
addToTileAtlas(ajaxResponses[i]);
}
return tileAtlas;
};
function loadAtlases() {
// When loadAtlasJSON has completed, call tileAtlasesReady.
// This also has the effect of resolving the value that tileAtlasesReady returns.
return when(loadAtlasJSON(), tileAtlasesReady);
}
// Return an object containing a function that can load the atlases
return {
loadAtlases: loadAtlases
};
});
И ваше приложение (или моя поддельная демонстрация) может использовать этот код следующим образом:
// --------------------------------------------------
// App code
// --------------------------------------------------
require(["tile-atlas"], function (atlas) {
console.log(atlas);
// The then() callback will only fire when loadAtlases is complete
atlas.loadAtlases().then(function (atlases) {
console.log("atlases loaded");
for (var id in atlases) {
console.log("atlas " + id, atlases[id]);
}
});
});
И выгружает следующую информацию на консоль:
Object {loadAtlases: function}
resolved Object {fileName: "tilesheets/ground-collision.json"} 3186ms
resolved Object {fileName: "tilesheets/ground.json"} 5159ms
resolved Object {fileName: "tilesheets/objects-collision.json"} 6221ms
tileAtlasesReady [Array[3]]
addToTileAtlas Object {fileName: "tilesheets/ground.json", id: "id1"}
addToTileAtlas Object {fileName: "tilesheets/ground-collision.json", id: "id0"}
addToTileAtlas Object {fileName: "tilesheets/objects-collision.json", id: "id2"}
atlases loaded
atlas id1 Object {fileName: "tilesheets/ground.json", id: "id1"}
atlas id0 Object {fileName: "tilesheets/ground-collision.json", id: "id0"}
atlas id2 Object {fileName: "tilesheets/objects-collision.json", id: "id2"}