2011-11-06 15 views
11

Ho bisogno di usare una funzione javascript da un file js esterno all'interno di un altro file js. Questo è fondamentalmente il codice che ho provato:Posso usare jquery getScript() senza un callback?

$.getScript('js/myHelperFile.js'); 
myHelperFunction(); 

questo non funziona - ottengo un errore di "myHelperFunction non è definita".

Tuttavia, questo codice funziona:

$.getScript('js/myHelperFile.js', function(){myHelperFunction();}); 

particolare mi vogliono essere in grado di farlo il primo modo - caricare il file nella parte superiore del mio file, e quindi utilizzare tutte le funzioni di cui ho bisogno da lì . È possibile, o sto fraintendendo il modo in cui funziona getScript?

risposta

29

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.

+0

Risposta incredibilmente completa, ben fatta! – GregL

+0

+! una risposta abbastanza completa. In ogni caso, se si sta iterando con una sintassi 'for..in' quando si carica jQuery? –

+0

@ 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. –

2

Utilizzando la funzione getScript() a sé stante, non penso che sia possibile. Il problema è che lo script viene caricato utilizzando AJAX (infatti, getScript è solo una scorciatoia per una speciale funzione $.ajax()) e quando il browser tenta di eseguire la funzione sulla riga successiva, forse il server non ha ancora restituito il file.

Se non si desidera utilizzare una funzione di callback, è necessario utilizzare la funzione di jQuery $.ajax() nella sua forma originale (e dimenticare il getScript()), e impostare il trasferimento come sincrono. In questo modo, si può essere sicuri che lo script verrà caricato prima dell'esecuzione del codice prosegue:

$.ajax({ 
    url: url, 
    dataType: "script", 
    async: false 
}); 
2

$ .getScript, e davvero tutte le funzioni jQuery legate OP HTTP sono non bloccanti chiamate per impostazione predefinita. Ciò significa che il codice non si blocca mentre l'operazione è completata. Ciò significa che il tuo codice sopra fallirà sicuramente perché non c'è modo in cui $ .getScript caricherà lo script prima che la linea che tenta di eseguire il metodo sia in esecuzione. Questo è il motivo per cui le funzioni di callback sono accettate come parametri.

Tuttavia, quello che stai cercando di fare è possibile. Una volta caricato lo script dovresti essere in grado di chiamarlo da qualsiasi luogo. Tutto ciò che è necessario per evitare che il metodo venga chiamato prima dello script in fase di download sta impostando i flag necessari.

var HelperFileLoaded = false; 
$.getScript('js/myHelperFile.js', function() { HelperFileLoaded = true; }); 

// ... 

function btnSaveClick() { 
    if(!HelperFileLoaded) return; 
    myHelperFunction(); 
} 

suggerimento di BenM è possibile, ma a meno che non si sta solo facendo una singola chiamata a myHelperFunction non funzionerà come una soluzione perché non ci sarà alcuna garanzia che lo script sarà scaricato prima di qualsiasi altro metodo di chiamata myHelperFunction hanno stato attivato.

0

Il primo funziona bene se si utilizza JSONP, lo faccio spesso.

Problemi correlati