J'écris un moteur qui nécessite l'utilisation de getScript assez souvent. Je l'ai poussée dans sa propre fonction, pour la facilité d'utilisation, mais je dois maintenant m'assurer que la fonction elle-même est synchrone. Malheureusement, je n'arrive pas à faire attendre à getScript que le chargement du script chargé soit terminé avant de poursuivre. J'ai même essayé de définir la propriété ajax asynch de jQuery sur false avant de passer l'appel. Je pense utiliser le protocole when/done de jQuery, mais je n'arrive pas à comprendre la logique qui consiste à l'insérer dans une fonction et à la rendre synchrone. Toute aide serait très appréciée!
function loadScript(script){
//Unrelated stuff here!!!
$.when(
$.getScript(script,function(){
//Unrelated stuff here!!!
})).done(function(){
//Wait until done, then finish function
});
}
Code de boucle (sur demande):
for (var i in divlist){
switch($("#"+divlist[i]).css({"background-color"})){
case #FFF:
loadScript(scriptlist[0],divlist[i]);
break;
case #000:
loadScript(scriptlist[2],divlist[i]);
break;
case #333:
loadScript(scriptlist[3],divlist[i]);
break;
case #777:
loadScript(scriptlist[4],divlist[i]);
break;
}
}
Comme je l'ai dit, il est relativement facile d'enchaîner les appels Ajax avec des objets de promesse. Maintenant, il ne voit pas pourquoi les scripts doivent être chargés les uns après les autres, mais vous en aurez une raison.
Tout d’abord, j’éliminerais l’instruction switch
si vous appelez la même fonction avec des arguments différents. Par exemple. vous pouvez mettre toutes les URL de script dans une carte:
var scripts = {
'#FFF': '...',
'#000': '...'
// etc.
};
Vous pouvez enchaîner des promesses en renvoyant simplement une autre promesse d'un rappel passé à .then
[docs] . Tout ce que vous avez à faire est de commencer par une promesse ou un objet différé:
var deferred = new $.Deferred();
var promise = deferred.promise();
for (var i in divlist) {
// we need an immediately invoked function expression to capture
// the current value of the iteration
(function($element) {
// chaining the promises,
// by assigning the new promise to the variable
// and returning a promise from the callback
promise = promise.then(function() {
return loadScript(
scripts[$element.css("background-color")],
$element
);
});
}($('#' + divlist[i])));
}
promise.done(function() {
// optional: Do something after all scripts have been loaded
});
// Resolve the deferred object and trigger the callbacks
deferred.resolve();
Dans loadScript
, vous retournez simplement la promesse retournée de $.getScript
ou celle renvoyée par .done
:
function loadScript(script_url, $element){
// Unrelated stuff here!!!
return $.getScript(script_url).done(function(){
// Unrelated stuff here
// do something with $element after the script loaded.
});
}
Les scripts seront tous appelés dans l'ordre où ils sont accessibles dans la boucle. Notez que si divlist
est un tableau, vous devez utiliser une boucle for
normale au lieu d’une boucle for...in
.
Cela a fonctionné pour moi et peut vous aider.
$.ajax({
async: false,
url: "jui/js/jquery-ui-1.8.20.min.js",
dataType: "script"
});
Fondamentalement, je viens de contourner la notation abrégée et ajouté dans le async: false
Savez-vous que $.getScript
accepte une fonction de rappel appelée de manière synchrone après le chargement du script?
Exemple:
$.getScript(url,function(){
//do after loading script
});
J'ai 2 autres solutions: un pur js un et un pour plusieurs js charge .
Essayez de cette façon, créez un tableau avec des objets différés et utilisez $ .when avec "apply"
var scripts = [
'src/script1.js',
'src/script2.js'
];
var queue = scripts.map(function(script) {
return $.getScript(script);
});
$.when.apply(null, queue).done(function() {
// Wait until done, then finish function
});
var getScript = function(url) {
var s = document.createElement('script');
s.async = true;
s.src = url;
var to = document.getElementsByTagName('script')[0];
to.parentNode.insertBefore(s, to);
};
La réponse de @Felix Kling a été un bon début. Cependant, j'ai découvert qu'il y avait un léger problème avec l'ensemble .done()
attaché à la fin du résultat retourné .getScripts()
si je voulais le "fonctionnaliser". Vous avez besoin de la dernière promesse des itérations chaînées .getScript()
de la boucle. Voici la version modifiée de sa solution (merci, BTW).
(function ($) {
var fetched = new function () {
this.scripts = [];
this.set = [];
this.exists = function (url) {
var exists = false;
$.each(this.set, function (index, value) {
if ((url || '') === value) {
exists = true;
return false;
}
});
return exists;
};
this.buildScriptList = function () {
var that = this;
that.set = [];
$('script').each(function () {
var src = $(this).attr('src') || false;
if (src) {
that.set.Push(src);
}
});
$.merge(this.set, this.scripts);
return this;
};
},
getScript = $.getScript;
$.getScript = function () {
var url = arguments[0] || '';
if (fetched.buildScriptList().exists(url)) {
return $.Deferred().resolve();
}
return getScript
.apply($, arguments)
.done(function () {
fetched.scripts.Push(url);
});
};
$.extend({
getScripts: function (urls, cache) {
if (typeof urls === 'undefined') {
throw new Error('Invalid URL(s) given.');
}
var deferred = $.Deferred(),
promise = deferred.promise(),
last = $.Deferred().resolve();
if (!$.isArray(urls)) {
urls = [urls];
}
$.each(urls, function (index) {
promise = promise.then(function () {
last = $.getScript(urls[index]);
return last;
});
});
if (Boolean(cache || false) && !Boolean($.ajaxSetup().cache || false)) {
$.ajaxSetup({cache: true});
promise.done(function () {
$.ajaxSetup({cache: false});
});
}
deferred.resolve();
return last;
}
});
})($);
Vous pouvez ignorer la fonction extraite (je l'ai implémentée pour réduire le nombre d'appels redondants potentiels - c'est pourquoi j'ai détourné .getScript()
) et voir où la variable last
est définie dans la méthode .getScripts()
. La valeur par défaut est un objet différé résolu, de sorte que si le tableau urls est vide, il est transmis au résultat renvoyé auquel attacher l'appel .done()
extérieur. Sinon, il se verra inévitablement attribuer le dernier objet de promesse issu des appels .getScript()
chaînés et veillera ainsi à ce que tout reste synchrone de l'extérieur de la fonction.
Le renvoi de l'objet différé initialement créé ne fonctionnera pas si vous le résolvez avant de le renvoyer à l'invocateur (ce que vous êtes censé faire par documentation officielle de jQuery ).
function loadStuff(data) {
var version = {
'accounting': '1.2.3',
'vue': '1.2.3',
'vueChart': '1.2.3'
};
$.getScripts([
'https://cdnjs.cloudflare.com/ajax/libs/accounting.js/' + version.accounting + '/accounting.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/vue/' + version.vue + '/vue.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/vue-chartjs/' + version.vueChart + '/vue-chartjs.min.js'
], true)
.done(function () {
// do stuff
})
.fail(function () {
throw new Error('There was a problem loading dependencies.');
});
}