Wie geht JavaScript mit AJAX-Antworten im Hintergrund um?

139

Was passiert eigentlich im Hintergrund, da JavaScript in einem einzelnen Thread ausgeführt wird, nachdem eine AJAX-Anforderung gestellt wurde? Ich würde gerne einen tieferen Einblick bekommen, kann jemand etwas Licht ins Dunkel bringen?

Aziz Punjani
quelle
6
Eine ziemlich gute Beschreibung ist hier: stackoverflow.com/questions/2914161/ajax-multi-threaded
Jonathan M
3
JavaScript-Code ist Single-Threaded (außer für Web-Worker), aber nicht der Browser, in dem die JavaScript-Engine ausgeführt wird ...
Juan Mendes
@JuanMendes Wird JavaScript in einem Thread ausgeführt, während die Ereigniswarteschlange in einem anderen Thread ausgeführt wird?
Shaun Luttin
1
@ShaunLuttin Nein, die Ereigniswarteschlange ist das, was JavaScript startet
Juan Mendes

Antworten:

213

Unter dem Deckblatt hat Javascript eine Ereigniswarteschlange. Jedes Mal, wenn ein Javascript-Ausführungsthread beendet wird, wird überprüft, ob sich ein anderes zu verarbeitendes Ereignis in der Warteschlange befindet. Wenn dies der Fall ist, wird es aus der Warteschlange entfernt und dieses Ereignis ausgelöst (z. B. durch einen Mausklick).

Das native Code-Netzwerk, das sich unter dem Ajax-Aufruf befindet, weiß, wann die Ajax-Antwort abgeschlossen ist, und ein Ereignis wird zur Javascript-Ereigniswarteschlange hinzugefügt. Wie der native Code weiß, wann der Ajax-Aufruf ausgeführt wird, hängt von der Implementierung ab. Es kann mit Threads implementiert werden oder es kann auch selbst ereignisgesteuert sein (es spielt keine Rolle). Der Punkt der Implementierung ist, dass, wenn die Ajax-Antwort fertig ist, ein nativer Code weiß, dass sie fertig ist, und ein Ereignis in die JS-Warteschlange stellt.

Wenn zu diesem Zeitpunkt kein Javascript ausgeführt wird, wird das Ereignis sofort ausgelöst, wodurch der Ajax-Antworthandler ausgeführt wird. Wenn gerade etwas ausgeführt wird, wird das Ereignis verarbeitet, wenn der aktuelle Javascript-Ausführungsthread beendet ist. Die Javascript-Engine muss keine Abfrage durchführen. Wenn die Ausführung eines Teils von Javascript abgeschlossen ist, überprüft die JS-Engine nur die Ereigniswarteschlange, um festzustellen, ob noch etwas ausgeführt werden muss. In diesem Fall wird das nächste Ereignis aus der Warteschlange entfernt und ausgeführt (Aufruf einer oder mehrerer Rückruffunktionen, die für dieses Ereignis registriert sind). Befindet sich nichts in der Ereigniswarteschlange, hat der JS-Interpreter freie Zeit (Speicherbereinigung oder Leerlauf), bis ein externer Agent etwas anderes in die Ereigniswarteschlange stellt und es erneut aktiviert.

Da alle externen Ereignisse die Ereigniswarteschlange durchlaufen und kein Ereignis jemals ausgelöst wird, während Javascript tatsächlich etwas anderes ausführt, bleibt es Single-Threaded.

Hier sind einige Artikel zu den Details:

jfriend00
quelle
Dank dafür. Ich vermutete, dass dies der Fall war, aber es ist sicher gut zu wissen. Ich habe eine for-Schleife, in der ich viele 'Ajax'-Anfragen sende. In meinem Handler (für jede Anfrage - in beliebiger Reihenfolge zurückgegeben) führe ich Code aus, der einige Zeit dauern kann. Gut zu wissen, dass dies auf jeden Fall funktionieren sollte.
iPadDeveloper2011
4
@telandor - Ereignisse werden in FIFO-Reihenfolge ausgeführt (es gibt möglicherweise einige Randfallausnahmen, aber die Absicht ist FIFO). Einige Ereignisse werden etwas anders behandelt. Zum Beispiel häufen sich Mausbewegungsereignisse nicht in der Warteschlange (wahrscheinlich, weil sie die Warteschlange leicht überlaufen könnten). Wenn sich die Maus bewegt und sich bereits ein Mausbewegungsereignis in der Warteschlange befindet und sich keine anderen neueren Ereignisse in der Warteschlange befinden, wird es mit der neuesten Position aktualisiert, anstatt dass ein neues Ereignis hinzugefügt wird. Ich würde vermuten, dass Intervall-Timer-Ereignisse wahrscheinlich auch speziell behandelt werden, um zu vermeiden, dass sie sich in der Warteschlange häufen.
jfriend00
2
@telandor - was musst du weiter erklären? Es ist FIFO. Ich habe meiner Antwort einige weitere Referenzartikel hinzugefügt. Die einzigen mir bekannten FIFO-Ausführungen sind sofort ausgelöste Ereignisse. Sie rufen .focus()ein Objekt auf und dies löst einige andere Ereignisse aus, z. B. ein "Unschärfe" -Ereignis für das Objekt mit Fokus. Dieses Unschärfeereignis geschieht synchron und durchläuft die Ereigniswarteschlange nicht, sodass es sofort vor anderen Dingen in der Ereigniswarteschlange auftritt. In der Praxis habe ich dies nie als praktisches Problem empfunden.
jfriend00
2
@telandor - Es gibt nicht mehrere Warteschlangen pro Browserdokument. Es gibt eine Warteschlange und alles geht seriell FIFO rein / raus. Timeouts und Ajax-Antworten sowie Maus- und Tastaturereignisse befinden sich also alle in derselben Warteschlange. Was zuerst in die Warteschlange gestellt wurde, wird zuerst ausgeführt.
jfriend00
1
@CleanCrispCode - Thx. Ich habe es als nützliche Referenz zu meiner Antwort hinzugefügt.
jfriend00
16

Sie können finden hier eine sehr vollständige Dokumentation über die Ereignisse in Javascript Handhabung.
Es wurde von einem Mann geschrieben, der an der Javascript-Implementierung im Opera-Browser arbeitet.

Schauen Sie sich die Titel genauer an: "Ereignisablauf", "Ereigniswarteschlange" und "Nichtbenutzerereignisse": Sie werden Folgendes erfahren:

  1. Javascript wird in einem einzelnen Thread für jede Browserregisterkarte oder jedes Browserfenster ausgeführt.
  2. Ereignisse werden nacheinander in die Warteschlange gestellt und ausgeführt.
  3. XMLHttpRequest werden von der Implementierung ausgeführt und Rückrufe werden über die Ereigniswarteschlange ausgeführt.

Hinweis: Ursprünglicher Link war: Link , ist aber jetzt tot.

qwertzguy
quelle
1

Ich möchte etwas näher auf die in den Antworten erwähnte Ajax-Implementierung eingehen.

Obwohl (regular) ist Javascript Ausführung nicht multi-threaded - wie auch in den oben genannten Antworten festgestellt - aber die eigentliche Umgang mit der AJAX responses(wie auch die Anforderungsbehandlung) ist nicht Javascript, und es - in der Regel - ist multi-threaded. (Siehe die Implementierung von XMLHttpRequest in Chromquellen, auf die wir oben eingehen werden.)

und ich erkläre, nehmen wir den folgenden Code:

var xhr = new XMLHttpRequest();

var t = Date.now;
xhr.open( "GET", "https://swx.cdn.skype.com/shared/v/1.2.15/SkypeBootstrap.min.js?v="+t(), true );

xhr.onload = function( e ) {
		console.log(t() + ': step 3');
    
    alert(this.response.substr(0,20));
};
console.log(t() + ': step 1');
xhr.send();
console.log(t() + ': step 2');

after an AJAX request is made(- nach Schritt 1), während Ihr js-Code weiter ausgeführt wird (Schritt 2 und danach), beginnt der Browser mit der eigentlichen Arbeit von: 1. Formatieren einer TCP-Anfrage 2. Öffnen eines Sockets 3. Senden von Headern 4. Handshaking 5. Senden body 6. wartende Antwort 7. Lesen von Headern 8. Lesen von body usw. Die gesamte Implementierung wird normalerweise in einem anderen Thread parallel zur Ausführung Ihres js-Codes ausgeführt. Zum Beispiel verwendet die erwähnte Chrom-Implementierung Threadable Loader go digg-into 😉 (Sie können sich auch einen Eindruck verschaffen, indem Sie auf die Registerkarte "Netzwerk" einer Seitenladung schauen, und Sie sehen einige gleichzeitige Anforderungen).

Abschließend würde ich sagen, dass - zumindest - die meisten Ihrer E / A-Vorgänge gleichzeitig / asynchron ausgeführt werden können (und Sie können dies beispielsweise mit einem Warten nutzen ). Alle Interaktionen mit diesen Operationen (die Ausgabe, die js-Rückrufausführung) sind jedoch alle synchron.

Shmulik Friedman
quelle