jQuery: charger les scripts dans l'ordre
je suis en train de charger des scripts de façon dynamique avec jQuery:
var scripts = ['script1.js','script2.js','script3.js'];
$.each(scripts , function(i, val) {
$.getScript(val, function() {
console.log('loaded '+ val);
});
mais parfois l'ordre des scripts chargés change. Comment puis-je charger chaque script après que le précédent a été chargé avec succès?
6 réponses
vous pouvez charger chacun d'eux une fois que le chargement précédent est terminé en utilisant la fonction de rappel de $.getScript()
comme appel de fonction récursif.
//setup array of scripts and an index to keep track of where we are in the process
var scripts = ['script1.js','script2.js','script3.js'],
index = 0;
//setup a function that loads a single script
function load_script() {
//make sure the current index is still a part of the array
if (index < scripts.length) {
//get the script at the current index
$.getScript(scripts[index], function () {
//once the script is loaded, increase the index and attempt to load the next script
console.log('Loaded: ' + scripts[index]);
index++;
load_script();
});
}
}
ce qui se passe dans votre code est que les scripts sont demandés en même temps et puisqu'ils se chargent de façon asynchrone, ils reviennent et s'exécutent dans un ordre aléatoire.
mise à Jour
Je n'ai pas testé cela, mais si les scripts sont hébergés localement, alors vous pouvez essayer de les récupérer en texte clair, puis stocker tout le code de variables jusqu'à ce qu'ils sont tous chargés à laquelle vous pourriez évaluer les scripts dans l'ordre:
var scripts = ['script1.js','script2.js','script3.js'],
//setup object to store results of AJAX requests
responses = {};
//create function that evaluates each response in order
function eval_scripts() {
for (var i = 0, len = scripts.length; i < len; i++) {
eval(responses[scripts[i]]);
}
}
$.each(scripts, function (index, value) {
$.ajax({
url : scripts[index],
//force the dataType to be `text` rather than `script`
dataType : 'text',
success : function (textScript) {
//add the response to the `responses` object
responses[value] = textScript;
//check if the `responses` object has the same length as the `scripts` array,
//if so then evaluate the scripts
if (responses.length === scripts.length) { eval_scripts(); }
},
error : function (jqXHR, textStatus, errorThrown) { /*don't forget to handle errors*/ }
});
});
vous faites usage du fait le jqXhr
objet retournée par $.getScript
(et toutes les autres méthodes Ajax) implémente l'interface Promise et fournit donc .pipe()
qui peut être utilisé pour enchaîner des objets différés:
var deferred = new $.Deferred(),
pipe = deferred;
$.each(scripts , function(i, val) {
pipe = pipe.pipe(function() {
return $.getScript(val, function() {
console.log('loaded '+ val);
});
});
});
deferred.resolve();
pour plus d'information, consultez différés objets et deferred.pipe
.
dans l'ensemble, l'utilisation d'objets différés vous donne une plus grande flexibilité et pourrait vous permettre d'ajouter des gestionnaires d'erreurs plus facilement.
amélioré version de la solution de @Jasper:
- en utilisant
$.when
pour synchroniser les appels. - à l'aide d'un global eval.
Voici le code:
/**
* Load scripts in parallel keeping execution order.
* @param {array} An array of script urls. They will parsed in the order of the array.
* @returns {$.Deferred}
*/
function getScripts(scripts) {
var xhrs = scripts.map(function(url) {
return $.ajax({
url: url,
dataType: 'text',
cache: true
});
});
return $.when.apply($, xhrs).done(function() {
Array.prototype.forEach.call(arguments, function(res) {
eval.call(this, res[0]);
});
});
}
certains scripts peuvent varier en taille, donc c'est imprévisible. Essayez quelque chose comme ça.
var scripts = ['script1.js','script2.js','script3.js'], ScriptIndex = 0;
function LoadScript(index){
if(index >= scripts.length)
return;
$.getScript(scripts[index], function(){
console.log('Loaded script '+ scripts[index]);
LoadScript(++ScriptIndex);
}
}
Cela devrait charger chaque script après le dernier est complètement chargé. Assurez-vous de le démarrer en appelant la fonction LoadScript(0);
Vous pouvez trier () le tableau -
var sorted = scripts.sort();
et ensuite utiliser trié dans votre chaque fonction.
il y a un petit problème avec la solution de @ngryman; s'il n'y a qu'une seule url dans le tableau des URL à charger, la .done () arguments de la fonction param sera un tableau unidimensionnel, plutôt que 2 dimensionnel. Vous aurez besoin de vérifier les arguments avant de passer à travers, ainsi:
/**
* Load scripts in parallel keeping execution order.
* @param {array} An array of script urls. They will parsed in the order of the array.
* @returns {$.Deferred}
*/
function getScripts(scripts) {
var xhrs = scripts.map(function(url) {
return $.ajax({
url: url,
dataType: 'text',
cache: true
});
});
return $.when.apply($, xhrs).done(function() {
if (arguments.length == 3 && typeof arguments[0] == "string") {
arguments = [arguments];
}
Array.prototype.forEach.call(arguments, function(res) {
eval.call(this, res[0]);
});
});
}
excuses pour la nouvelle réponse, pas assez de rep pour poster un commentaire sur la réponse de @ngryman, mais j'ai pensé que cela pourrait aider quelqu'un!