webentwicklung-frage-antwort-db.com.de

document.createElement ("script") synchron

Ist es möglich, ein .js Datei synchron und sofort danach benutzen?

<script type="text/javascript">
    var head = document.getElementsByTagName('head').item(0);
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('src', 'http://mysite/my.js');
    head.appendChild(script);

    myFunction(); // Fails because it hasn't loaded from my.js yet.

    window.onload = function() {
        // Works most of the time but not all of the time.
        // Especially if my.js injects another script that contains myFunction().
        myFunction();
    };
</script>

Dies ist vereinfacht. In meiner Implementierung befindet sich das createElement-Zeug in einer Funktion. Ich dachte darüber nach, der Funktion etwas hinzuzufügen, mit dem überprüft werden kann, ob eine bestimmte Variable instanziiert wurde, bevor die Kontrolle zurückgegeben wird. Aber dann gibt es immer noch das Problem, was zu tun ist, wenn ich js von einer anderen Site einbinde, über die ich keine Kontrolle habe.

Gedanken?

Bearbeiten:

Ich habe die beste Antwort fürs Erste akzeptiert, weil sie eine gute Erklärung dafür gibt, was los ist. Aber wenn jemand Vorschläge hat, wie man das verbessern kann, bin ich offen für sie. Hier ist ein Beispiel dafür, was ich gerne machen würde.

// Include() is a custom function to import js.
Include('my1.js');
Include('my2.js');

myFunc1('blarg');
myFunc2('bleet');

Ich möchte nur verhindern, dass ich die Interna zu genau kenne und einfach sagen kann: "Ich möchte dieses Modul verwenden, und jetzt werde ich Code daraus verwenden."

71
Josh Johnson

Sie können Ihr <script> - Element mit einem "Onload" - Handler erstellen. Dieser wird aufgerufen, wenn das Skript vom Browser geladen und ausgewertet wurde.

var script = document.createElement('script');
script.onload = function() {
  alert("Script loaded and ready");
};
script.src = "http://whatever.com/the/script.js";
document.getElementsByTagName('head')[0].appendChild(script);

Sie können es nicht synchron machen.

edit - Es wurde darauf hingewiesen, dass IE) kein "load" -Ereignis für <script> - Tags auslöst, die geladen werden Ich nehme an, das nächste was zu tun wäre, das Skript mit einem XMLHttpRequest abzurufen und dann eval() es selbst. (Oder ich nehme an, den Text in ein <script> - Tag zu stopfen Sie fügen hinzu, dass die Ausführungsumgebung von eval() vom lokalen Gültigkeitsbereich abhängt und daher nicht unbedingt das tut, was Sie möchten.)

edit - Ab Anfang 2013 empfehle ich dringend, ein robusteres Tool zum Laden von Skripten wie - zu suchen. Requirejs . Es gibt viele Sonderfälle, über die man sich Sorgen machen muss. Für wirklich einfache Situationen gibt es yepnope , das jetzt in Modernizr eingebaut ist.

117
Pointy

Das ist nicht schön, aber es funktioniert:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
</script>

<script type="text/javascript">
  functionFromOther();
</script>

Oder

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  window.onload = function() {
    functionFromOther();
  };
</script>

Das Skript muss entweder in einem separaten <script> - Tag oder vor window.onload() enthalten sein.

Das wird nicht funktionieren:

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"></script>');
  functionFromOther(); // Error
</script>

Dasselbe kann mit dem Erstellen eines Knotens geschehen, wie es Pointy getan hat, jedoch nur in FF. Sie können nicht garantieren, wann das Skript in anderen Browsern verfügbar sein wird.

Als XML-Purist hasse ich das wirklich. Aber es funktioniert vorhersehbar. Sie können diese hässlichen document.write() s leicht einwickeln, damit Sie sie nicht ansehen müssen. Sie können sogar Tests durchführen, einen Knoten erstellen und ihn anhängen und dann auf document.write() zurückgreifen.

23
Josh Johnson

Dies ist viel zu spät, aber für die zukünftige Bezugnahme auf jeden, der dies tun möchte, können Sie Folgendes verwenden:

function require(file,callback){
    var head=document.getElementsByTagName("head")[0];
    var script=document.createElement('script');
    script.src=file;
    script.type='text/javascript';
    //real browsers
    script.onload=callback;
    //Internet Explorer
    script.onreadystatechange = function() {
        if (this.readyState == 'complete') {
            callback();
        }
    }
    head.appendChild(script);
}

Ich habe vor einiger Zeit einen kurzen Blogeintrag dazu verfasst http://crlog.info/2011/10/06/dynamisch-anforderneinschließen-a-javascript-datei-in-a-seite-und-notifiziert- -wenn-es-geladen /

16
zcourts

Asynchrone Programmierung ist etwas komplizierter kompliziert , da die Konsequenz einer Anforderung in einer Funktion gekapselt ist, anstatt der Anforderungsanweisung zu folgen. aber das Echtzeitverhalten, das der Benutzer erleben kann deutlich besser weil sie Wenn ein träger Server oder ein träger Netzwerk nicht erkannt wird, verhält sich der Browser so, als wäre er abgestürzt. Synchron Programmierung ist respektlos und sollte nicht verwendet werden in Anwendungen, die verwendet werden von Menschen.

Douglas Crockford ( YUI Blog )

In Ordnung, schnallen Sie Ihre Sitze an, denn es wird eine holprige Fahrt. Immer mehr Leute fragen nach dem dynamischen Laden von Skripten über Javascript, es scheint ein heißes Thema zu sein.

Die Hauptgründe, warum dies so beliebt wurde, sind:

  • clientseitige Modularität
  • einfacheres Abhängigkeitsmanagement
  • fehlerbehandlung
  • leistungsvorteile

Über Modularität : Es ist offensichtlich, dass die Verwaltung clientseitiger Abhängigkeiten direkt auf der Clientseite erfolgen sollte. Wenn ein bestimmtes Objekt, Modul oder eine Bibliothek benötigt wird, fragen wir einfach danach und laden sie dynamisch.

Fehlerbehandlung : Wenn eine Ressource ausfällt, haben wir immer noch die Möglichkeit, nur die Teile zu blockieren, die vom betroffenen Skript abhängen, oder es mit einiger Verzögerung erneut zu versuchen.

Performance hat sich zu einem Wettbewerbsvorteil zwischen Websites entwickelt und ist jetzt ein Suchrankingfaktor. Dynamische Skripte können asynchrones Verhalten im Gegensatz zu der standardmäßigen Blockierungsmethode für den Umgang mit Skripten im Browser imitieren. Scripts blockieren andere Ressourcen, Scripts blockieren weitere Analyse des HTML-Dokuments, Scripts blockieren die Benutzeroberfläche. Jetzt können Sie mit dynamischen Skript-Tags und ihren browserübergreifenden Alternativen echte asynchrone Anforderungen ausführen und abhängigen Code nur ausführen, wenn sie verfügbar sind. Ihre Skripte werden auch mit anderen Ressourcen parallel geladen und das Rendering ist fehlerfrei.

Der Grund, warum manche Leute an synchronen Skripten festhalten, ist, dass sie daran gewöhnt sind. Sie denken, es ist der Standardweg, es ist der einfachere Weg, und manche denken vielleicht sogar, es ist der einzige Weg.

Das Einzige, worüber wir uns Gedanken machen sollten, wenn dies in Bezug auf das Design einer Anwendung entschieden werden muss, ist die Endbenutzererfahrung . Und in diesem Bereich ist asynchron nicht zu schlagen. Der Benutzer erhält unmittelbare Antworten (oder sagt Versprechen), und ein Versprechen ist immer besser als nichts. Ein leerer Bildschirm erschreckt die Menschen. Entwickler sollten nicht faul sein, wahrgenommene Leistung zu verbessern.

Und zum Schluss noch ein paar Worte zur schmutzigen Seite. Was Sie tun sollten, damit es browserübergreifend funktioniert:

  1. lernen, asynchron zu denken
  2. ordnen Sie Ihren Code so an, dass er modular ist
  3. ordnen Sie Ihren Code so an, dass Fehler und Edge-Fälle gut behandelt werden
  4. schrittweise verbessern
  5. achten Sie immer auf das richtige Feedback
7
gblazex

Die obigen Antworten wiesen mich in die richtige Richtung. Hier ist eine allgemeine Version von dem, was ich zum Arbeiten gebracht habe:

  var script = document.createElement('script');
  script.src = 'http://' + location.hostname + '/module';
  script.addEventListener('load', postLoadFunction);
  document.head.appendChild(script);

  function postLoadFunction() {
     // add module dependent code here
  }      
4
James

Dies sieht nach einem anständigen Überblick über das Laden dynamischer Skripte aus: http://unixpapa.com/js/dyna.html

3
morgancodes

Ich hatte die folgenden Probleme mit den vorhandenen Antworten auf diese Frage (und Variationen dieser Frage in anderen Stackoverflow-Threads):

  • Keiner der geladenen Codes war debuggbar
  • Viele der Lösungen erforderten Rückrufe, um zu wissen, wann der Ladevorgang abgeschlossen war, anstatt wirklich zu blockieren. Das bedeutet, dass ich Ausführungsfehler erhalten würde, wenn ich sofort den geladenen Code aufrufe.

Oder etwas genauer:

  • Keiner der geladenen Codes war debuggbar (mit Ausnahme des HTML-Skript-Tag-Blocks, wenn und nur wenn die Lösung dem dom Skriptelemente hinzugefügt hat, und niemals als einzelne anzeigbare Skripte.) => Angesichts der Anzahl der Skripte, die ich laden (und debuggen) muss, war dies nicht akzeptabel.
  • Lösungen, die Ereignisse 'onreadystatechange' oder 'onload' verwenden, konnten nicht blockiert werden. Dies war ein großes Problem, da der Code ursprünglich dynamische Skripte synchron mit 'require ([filename,' dojo/domReady ']);' und ich strippte Dojo aus.

Meine endgültige Lösung, bei der das Skript vor der Rückkehr geladen wird UND alle Skripte im Debugger ordnungsgemäß verfügbar sind (zumindest für Chrome)), lautet wie folgt:

WARNUNG: Der folgende Code sollte nur im Entwicklungsmodus verwendet werden. (Für den Freigabemodus empfehle ich Vorverpackung und Minimierung OHNE dynamisches Laden von Skripten oder unter zumindest ohne eval).

//Code User TODO: you must create and set your own 'noEval' variable

require = function require(inFileName)
{
    var aRequest
        ,aScript
        ,aScriptSource
        ;

    //setup the full relative filename
    inFileName = 
        window.location.protocol + '//'
        + window.location.Host + '/'
        + inFileName;

    //synchronously get the code
    aRequest = new XMLHttpRequest();
    aRequest.open('GET', inFileName, false);
    aRequest.send();

    //set the returned script text while adding special comment to auto include in debugger source listing:
    aScriptSource = aRequest.responseText + '\n////# sourceURL=' + inFileName + '\n';

    if(noEval)//<== **TODO: Provide + set condition variable yourself!!!!**
    {
        //create a dom element to hold the code
        aScript = document.createElement('script');
        aScript.type = 'text/javascript';

        //set the script tag text, including the debugger id at the end!!
        aScript.text = aScriptSource;

        //append the code to the dom
        document.getElementsByTagName('body')[0].appendChild(aScript);
    }
    else
    {
        eval(aScriptSource);
    }
};
3
function include(file){
return new Promise(function(resolve, reject){
        var script = document.createElement('script');
        script.src = file;
        script.type ='text/javascript';
        script.defer = true;
        document.getElementsByTagName('head').item(0).appendChild(script);

        script.onload = function(){
        resolve()
        }
        script.onerror = function(){
          reject()
        }
      })

 /*I HAVE MODIFIED THIS TO  BE PROMISE-BASED 
   HOW TO USE THIS FUNCTION 

  include('js/somefile.js').then(function(){
  console.log('loaded');
  },function(){
  console.log('not loaded');
  })
  */
}
3
Daggie Blanqx

Ich bin es gewohnt, mehrere .js-Dateien auf meiner Website zu haben, die voneinander abhängen. Um sie zu laden und sicherzustellen, dass die Abhängigkeiten in der richtigen Reihenfolge ausgewertet werden, habe ich eine Funktion geschrieben, die alle Dateien lädt und sie dann, sobald sie alle empfangen wurden, eval(). Der Hauptnachteil ist, dass dies mit CDN nicht funktioniert. Für solche Bibliotheken (z. B. jQuery) ist es besser, sie statisch einzuschließen. Beachten Sie, dass das Einfügen von Skriptknoten in HTML dynamisch nicht garantiert, dass Skripts in der richtigen Reihenfolge ausgewertet werden, zumindest nicht in Chrome (dies war der Hauptgrund für Schreiben dieser Funktion).

function xhrs(reqs) {
  var requests = [] , count = [] , callback ;

  callback = function (r,c,i) {
    return function () {
      if  ( this.readyState == 4 ) {
        if (this.status != 200 ) {
          r[i]['resp']="" ;
        } 
        else {
          r[i]['resp']= this.responseText ;
        }
        c[0] = c[0] - 1 ;
        if ( c[0] == 0 ) {
          for ( var j = 0 ; j < r.length ; j++ ) {
            eval(r[j]['resp']) ;
          }
        }
      }
    }
  } ;
  if ( Object.prototype.toString.call( reqs ) === '[object Array]' ) {
    requests.length = reqs.length ;
  }
  else {
    requests.length = 1 ;
    reqs = [].concat(reqs);
  }
  count[0] = requests.length ;
  for ( var i = 0 ; i < requests.length ; i++ ) {
    requests[i] = {} ;
    requests[i]['xhr'] = new XMLHttpRequest () ;
    requests[i]['xhr'].open('GET', reqs[i]) ;
    requests[i]['xhr'].onreadystatechange = callback(requests,count,i) ;
    requests[i]['xhr'].send(null);
  }
}

Ich habe nicht herausgefunden, wie man auf denselben Wert verweist, ohne ein Array zu erstellen (für count). Ansonsten halte ich es für selbsterklärend (wenn alles geladen ist, eval() jede Datei in der angegebenen Reihenfolge, ansonsten nur die Antwort speichern).

Anwendungsbeispiel:

xhrs( [
       root + '/global.js' ,
       window.location.href + 'config.js' ,
       root + '/js/lib/details.polyfill.min.js',
       root + '/js/scripts/address.js' ,
       root + '/js/scripts/tableofcontents.js' 
]) ;
1
user1251840

Ironischerweise habe ich das, was Sie wollen, aber ich möchte etwas, das näher an dem liegt, was Sie hatten.

Ich lade Dinge dynamisch und asynchron, aber mit einem load Rückruf wie folgt (mit dojo und xmlhtpprequest)

  dojo.xhrGet({
    url: 'getCode.php',
    handleAs: "javascript",
    content : {
    module : 'my.js'
  },
  load: function() {
    myFunc1('blarg');
  },
  error: function(errorMessage) {
    console.error(errorMessage);
  }
});

Für eine detailliertere Erklärung siehe hier

Das Problem ist, dass der Code irgendwo auf der Linie ausgewertet wird und wenn irgendetwas mit Ihrem Code nicht stimmt, die Anweisung console.error(errorMessage); die Zeile angibt, in der eval() steht, nicht der eigentliche Fehler. Dies ist so ein großes Problem, dass ich tatsächlich versuche, zurück in <script> - Anweisungen zu konvertieren (siehe hier .

0
puk