Facendo "il primo modo" non è attualmente * possibile con jQuery.getScript
perché supporta solo operazioni asincrone, e quindi ritorna immediatamente dopo essere stato chiamato.
Poiché ritorna prima che lo script sia stato scaricato quando lo script tenta di richiamare myHelperFunction()
, myHelperFunction
è undefined
e causa quell'errore.
*vedere l'aggiornamento in fondo a questa risposta per un nuovo metodo promettente che alla fine permetterà di scrivere codice che funziona quasi come il tuo stile desiderato.
Si potrebbe farlo utilizzando jQuery.ajax
con l'impostazione async
impostato false
con il codice che è qualcosa di simile:
$.ajax({
url: 'js/myHelperFile.js',
async: false,
dataType: "script",
});
myHelperFunction();
Ma come documentation stati:
richieste sincrone può bloccare temporaneamente il browser, disabilitando tutte le azioni mentre la richiesta è attiva.
Questa è una cosa molto brutta. Se il server che fornisce myHelperFile.js
è in grado di rispondere in qualsiasi momento (quale corrisponderà a a un certo punto) la pagina non risponderà fino a quando la richiesta non termina o scade.
Sarebbe un'idea molto migliore per abbracciare lo stile di callback che viene utilizzato pesantemente in jQuery.
$.getScript('js/myHelperFile.js', function() {
myHelperFunction();
});
Non è necessario utilizzare una funzione anonima sia, si può mettere il codice in una funzione denominata:
function doStuffWithHelper() {
myHelperFunction();
}
$.getScript('js/myHelperFile.js', doStuffWithHelper);
Se avete bisogno di caricare più di una dipendenza prima di chiamare myHelperFunction
non funzionerà a meno che non si configuri una sorta di sistema per capire se tutte le dipendenze sono state scaricate prima di eseguire il codice. È quindi possibile impostare il gestore di callback su tutte le chiamate .getScript
per verificare che tutte le dipendenze siano caricate prima di eseguire il codice.
var loadedScripts = {
myHelperFile: false,
anotherHelperFile: false
};
function doStuffWithHelper() {
// check to see if all our scripts are loaded
var prop;
for (prop in loadedScripts) {
if (loadedScripts.hasOwnProperty(prop)) {
if (loadedScripts[prop] === false) {
return; // not everything is loaded wait more
}
}
}
myHelperFunction();
}
$.getScript('js/myHelperFile.js', function() {
loadedScripts.myHelperFile = true;
doStuffWithHelper();
});
$.getScript('js/anotherHelperFile.js', function() {
loadedScripts.anotherHelperFile = true;
doStuffWithHelper();
});
Come potete vedere, questo tipo di approccio diventa veloce e intrattabile.
Se è necessario caricare più dipendenze, è preferibile utilizzare uno scriptloader come yepnope.js per farlo.
yepnope({
load : [ 'js/myHelperFile.js', 'js/anotherHelperFile.js' ],
complete: function() {
var foo;
foo = myHelperFunction();
foo = anotherHelperFunction(foo);
// do something with foo
}
});
Yepnope utilizza callback come solo .getScript
quindi non è possibile utilizzare lo stile sequenziale si voleva attaccare con. Questo è un buon compromesso, anche perché fare più chiamate sincrone su jQuery.ajax
in una riga significherebbe solo aggravare quei problemi con i metodi.
Aggiornamento 2013-12-08
Il jQuery 1.5 release, fornisce un altro modo puro jQuery di farlo. A partire dal 1.5, tutte le richieste di AJAX restituiscono un Deferred Object Così si potrebbe fare questo:
$.getScript('js/myHelperFile.js').done(function() {
myHelperFunction();
});
Essendo una richiesta asincrona, che ovviamente richiede un callback.Utilizzando Deferreds tuttavia ha un enorme vantaggio rispetto al sistema di richiamata tradizionali, utilizzando $.when() si potrebbe facilmente estendere questo per il caricamento di più script prerequisito senza il proprio sistema contorto di monitoraggio:
$.when($.getScript('js/myHelperFile.js'), $.getScript('js/anotherHelperFile.js')).done(function() {
var foo;
foo = myHelperFunction();
foo = anotherHelperFunction(foo);
// do something with foo
});
Questo scaricherebbe sia gli script contemporaneamente ed eseguire solo il callback dopo che entrambi sono stati scaricati, proprio come nell'esempio Yepnope sopra.
Aggiornamento 2014-03-30
ho letto di recente an article che mi ha reso consapevole di una nuova funzione JavaScript che potrebbero in futuro consentire il tipo di codice procedurale-cercando-ancora-async che volevi usare! Async Functions utilizzerà la parola chiave await
per sospendere l'esecuzione di una funzione async
fino al completamento dell'operazione asincrona. La cattiva notizia è che è attualmente previsto l'inserimento in ES7, due versioni di ECMAScript.
await
si basa sulla funzione asincrona che si sta aspettando restituendo un Promise. Non sono sicuro di come si comporteranno le implementazioni del browser, se richiedono un vero oggetto Promessa ES6 o se altre implementazioni di promesse come quella restituita da jQuery da chiamate AJAX saranno sufficienti. Se è necessario un ES6 promessa, è necessario avvolgere jQuery.getScript
con una funzione che restituisce una promessa:
"use strict";
var getScript = function (url) {
return new Promise(function(resolve, reject) {
jQuery.getScript(url).done(function (script) {
resolve(script);
});
});
};
Quindi si può utilizzare per scaricare e await
prima di procedere:
(async function() {
await getScript('js/myHelperFile.js');
myHelperFunction();
}());
Se siete davvero determinato a scrivere in questo stile oggi è possibile utilizzare asyc
e await
e quindi utilizzare Traceur per traspondere il codice in codice che verrà eseguito nei browser correnti. Il vantaggio ulteriore di questo è che potresti anche usare molti other useful new ES6 features.
Risposta incredibilmente completa, ben fatta! – GregL
+! una risposta abbastanza completa. In ogni caso, se si sta iterando con una sintassi 'for..in' quando si carica jQuery? –
@ gion_13 Mi consente di tornare dall'interno di 'if (loadedScripts [prop] === false) {' per uscire dalla funzione in modo che il seguente codice che si basa sulle dipendenze non venga eseguito. Se usassi '$ .each', avrei potuto interrompere il ciclo in anticipo con' return false', ma poi il codice dopo il ciclo sarebbe stato eseguito, avrei dovuto impostare qualche tipo di flag prima del ciclo e poi controllarlo dopo il ciclo per impedire che il codice venga eseguito. Usare il ciclo 'for..in' ed uscire dalla funzione è più pulito. Guardandolo ora, invece del dizionario, avrei usato un conteggio var e l'ho confrontato con gli script totali. –