2017-02-18 5 views
0

In meinem Projekt habe ich das ziemlich Standard Loadscript Methode JS-Skripte dynamisch zu laden, und wenn Sie fertig ist, wird der Rückruf gefeuert:Wie lade ich JS-Skripte dynamisch und gleichzeitig-sicher?

app.utilities.loadScript = function loadScript(url, callback) 
{ 

    if (app.scripts[url]===true) { 
    callback(); 
    } else { 

    var head = document.getElementsByTagName('head')[0]; 
    var script = document.createElement('script'); 
    script.type = 'text/javascript'; 
    script.src = url; 

    script.onload = function() { 
     app.scripts[url] = true; 
     callback(); 
    } 
    // Fire the loading 
    head.appendChild(script); 
    } 
}; 

In meinem Projekt Situation, ich bin mit einem komponentenbasierten System arbeiten . Stellen Sie sich zur Veranschaulichung des Problems zwei Google Maps-Komponenten auf derselben Seite vor. Beides sind Instanzen einer Komponente. In ihrem Konstruktor bin Aufruf ich die Methode, wie so ...

constructor(selector) { 

    super(selector); 

    // API load status 
    this._api = false; 
    // load the maps API 
    app.utilities.loadScript('https://maps.googleapis.com/maps/api/js?v=3.exp&key=' + app.config.mapsKey, this.initMap.bind(this)); 

    } 

ich die Situation zu erwarten, dass, wenn zwei Instanzen einer Komponente das gleiche Skript geladen werden, sollte es nicht zweimal geladen werden. Aus diesem Grund können Sie sehen, wie ich innerhalb des loadScript-Helpers ein globales Array von bereits geladenen Skripten verwalte: app.scripts [url] = true;

Diese Strategie funktioniert jedoch nicht, da mehrere loadScript-Aufrufe sehr nahe nacheinander gestartet werden. Da der erste noch nicht fertig ist (in Bearbeitung), startet der zweite Anruf einen weiteren.

Ich erwäge sofort das Setzen eines "Lade" -Status auf das Skript in einem globalen Array, aber das würde mich immer noch fragen lassen, wie der zweite Anruf auf den ersten hören kann, der bereit ist, weil es einen solchen Auslöser benötigt Andernfalls wird der Rückruf zu früh ausgelöst.

Ich habe das Gefühl, dass ich das vielleicht überdenke?

Edit: einschließlich der Code, der diese löst, basierend auf einer Antwort von @Siam, mit einem kleinen Kniff hinzugefügt:

app.utilities.loadScriptEvent = function loadScript(url, eventName) 
{ 

    if (app.scripts[url] == 'loading') { 
     return true; 
    } 

    else { 
    app.scripts[url] = 'loading'; 

    var event = new CustomEvent(eventName); 

    // Adding the script tag to the head as suggested before 
    var head = document.getElementsByTagName('head')[0]; 
    var script = document.createElement('script'); 
    script.type = 'text/javascript'; 
    script.src = url; 

    // Then bind the event to the callback function. 
    // There are several events for cross browser compatibility. 
    //script.onreadystatechange = callback; 

    script.onload = function() { 
     app.scripts[url] = true; 
     app.window.dispatchEvent(event); 
    } 
    // Fire the loading 
    head.appendChild(script); 
    } 
}; 

Beachten Sie, dass das Verfahren jetzt loadScriptEvent genannt wird, und dass der zweite Parameter ist ein Ereignisname, kein Rückruf. Die Idee ist, dass, obwohl zwei Komponenten fast gleichzeitig einen Anruf dazu machen könnten, beide auf dasselbe einzelne Ereignis hören. Das ist jedoch nicht genug, die zweite Anfrage muss noch gestoppt werden, wenn die erste bereits das Skript geladen hat. Das habe ich bei einem Ladezustand erkannt. Sie würden diese Methode so nennen:

app.utilities.loadScriptEvent('https://maps.googleapis.com/maps/api/js?v=3.exp&key=' + app.config.mapsKey, "gmloaded"); 
app.window.addEventListener('gmloaded',this.initMap.bind(this)); 

Dies funktioniert gut. Komponenteninstanzen kennen einander nicht, doch das Skript wird immer noch nur einmal asynchron geladen, und sofort können wir den Code ausführen.

+0

Verwendung 'setInterval' zu prüfen, ob die erste Anforderung abgeschlossen ist oder nicht, und wenn es zu initiieren ist dann die zweite und Deaktivieren Sie dieses Intervall – m87

+0

@siam. Ich hatte Angst vor einer solchen Antwort, aber es könnte der einzige Weg sein. Beachten Sie jedoch, dass die zweite Anfrage keine Kenntnis von der ersten hat (da dies zwei Komponenten-Instanzen sind, die einander nicht kennen). – Ferdy

+1

umm ... vielleicht gibt es einen anderen Weg. Sie können ein benutzerdefiniertes Ereignis erstellen, das beim Abschluss der ersten Anfrage ausgelöst wird. und Sie könnten auch die "Daten" der ersten Anfrage mit diesem Ereignis übergeben. – m87

Antwort

1

Der folgende Code könnte Ihnen eine grobe Vorstellung, wie es getan werden könnte:

// using jQuery 

app.utilities.loadScript = function loadScript(url, callback) { 
    ... 
    script.onload = function(data) { 
     app.scripts[url] = true; 
     callback(); 
     ... 
     $(document).trigger('firstRqstCompleted', data); 
    } 
    ... 
} 

$(document).on('firstRqstCompleted', function(e, data) { 
    // do stuff with first request's data 
    // initiate second request 
}); 
+0

Vielen Dank für den Code, aber leider löst diese Lösung das Problem nicht. Das Problem besteht darin, dass beide Aufrufe von Ladiescript sehr schnell nacheinander ausgeführt werden. Daher wird jeder Code, den Sie onload eingeben, die zweite Anfrage nicht daran hindern, dasselbe zu tun. Haben Sie dies mit einem benutzerdefinierten Event-Handler versucht, wird dieser zweimal aufgerufen. – Ferdy

+0

Ihr Code benötigt ein wenig zwicken (meine bearbeitete Antwort sehen) onload zu vermeiden, zweimal noch immer ausgelöst, aber die allgemeine Idee, eine Veranstaltung der Verwendung richtig war, so dass ich markiert es als die richtige Antwort. – Ferdy

+0

danke! froh, dass es geholfen hat. :) – m87

Verwandte Themen