Fabrícios Antwort ist genau richtig; aber ich wollte seine Antwort mit etwas weniger Technischem ergänzen, das sich auf eine Analogie konzentriert, um das Konzept der Asynchronität zu erklären .
Eine Analogie ...
Gestern erforderte meine Arbeit einige Informationen von einem Kollegen. Ich habe ihn angerufen; So lief das Gespräch ab:
Me : Hallo Bob, ich muss wissen , wie wir foo 'die d bar d letzte Woche. Jim möchte einen Bericht darüber, und du bist der einzige, der die Details darüber kennt.
Bob : Sicher, aber ich brauche ungefähr 30 Minuten.
Ich : Das ist großartig, Bob. Rufen Sie mich zurück, wenn Sie die Informationen haben!
An diesem Punkt legte ich den Hörer auf. Da ich Informationen von Bob benötigte, um meinen Bericht zu vervollständigen, verließ ich den Bericht und ging stattdessen auf einen Kaffee, um eine E-Mail zu erhalten. 40 Minuten später (Bob ist langsam) rief Bob zurück und gab mir die Informationen, die ich brauchte. An diesem Punkt nahm ich meine Arbeit mit meinem Bericht wieder auf, da ich alle Informationen hatte, die ich brauchte.
Stellen Sie sich vor, das Gespräch wäre stattdessen so verlaufen.
Me : Hallo Bob, ich muss wissen , wie wir foo 'die d bar d letzte Woche. Jim will einen Bericht darüber, und du bist der einzige, der die Details darüber kennt.
Bob : Sicher, aber ich brauche ungefähr 30 Minuten.
Ich : Das ist großartig, Bob. Ich werde warten.
Und ich saß da und wartete. Und wartete. Und wartete. Für 40 Minuten. Nichts tun als warten. Schließlich gab Bob mir die Informationen, wir legten auf und ich vervollständigte meinen Bericht. Aber ich hatte 40 Minuten Produktivität verloren.
Dies ist ein asynchrones vs. synchrones Verhalten
Genau das passiert in allen Beispielen unserer Frage. Das Laden eines Bildes, das Laden einer Datei von der Festplatte und das Anfordern einer Seite über AJAX sind allesamt langsame Vorgänge (im Kontext des modernen Computing).
Anstatt auf den Abschluss dieser langsamen Vorgänge zu warten , können Sie in JavaScript eine Rückruffunktion registrieren, die ausgeführt wird, wenn der langsame Vorgang abgeschlossen ist. In der Zwischenzeit führt JavaScript jedoch weiterhin anderen Code aus. Die Tatsache, dass JavaScript anderen Code ausführt , während auf den Abschluss des langsamen Vorgangs gewartet wird , führt dazu, dass das Verhalten asynchron ist . Wenn JavaScript gewartet hätte, bis der Vorgang abgeschlossen ist, bevor ein anderer Code ausgeführt wurde, wäre dies ein synchrones Verhalten gewesen.
var outerScopeVar;
var img = document.createElement('img');
// Here we register the callback function.
img.onload = function() {
// Code within this function will be executed once the image has loaded.
outerScopeVar = this.width;
};
// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);
Im obigen Code bitten wir Sie, JavaScript zu laden lolcat.png
. Dies ist eine langsame Operation. Die Rückruffunktion wird ausgeführt, sobald diese langsame Operation ausgeführt wurde. In der Zwischenzeit verarbeitet JavaScript die nächsten Codezeilen weiter. dh alert(outerScopeVar)
.
Aus diesem Grund wird die Warnung angezeigt undefined
. da der alert()
sofort verarbeitet wird, anstatt nachdem das bild geladen wurde.
Um unseren Code zu reparieren, müssen wir den alert(outerScopeVar)
Code nur in die Rückruffunktion verschieben. Infolgedessen brauchen wir die outerScopeVar
als globale Variable deklarierte Variable nicht mehr .
var img = document.createElement('img');
img.onload = function() {
var localScopeVar = this.width;
alert(localScopeVar);
};
img.src = 'lolcat.png';
Sie werden immer sehen, dass ein Rückruf als Funktion angegeben wird, da dies in JavaScript die einzige Möglichkeit ist, Code zu definieren, ihn jedoch erst später auszuführen.
Daher ist in allen unseren Beispielen function() { /* Do something */ }
der Rückruf; Um alle Beispiele zu korrigieren , müssen wir nur den Code, der die Antwort der Operation benötigt, dorthin verschieben!
* Technisch kann man das auch nutzen eval()
, ist aber eval()
böse für diesen Zweck
Wie lasse ich meinen Anrufer warten?
Möglicherweise haben Sie derzeit einen ähnlichen Code.
function getWidthOfImage(src) {
var outerScopeVar;
var img = document.createElement('img');
img.onload = function() {
outerScopeVar = this.width;
};
img.src = src;
return outerScopeVar;
}
var width = getWidthOfImage('lolcat.png');
alert(width);
Wir wissen jedoch jetzt, dass das return outerScopeVar
sofort passiert; bevor die onload
Rückruffunktion die Variable aktualisiert hat. Dies führt dazu, dass Sie getWidthOfImage()
zurückkehren undefined
und undefined
alarmiert werden.
Um dies zu beheben, müssen wir zulassen, dass die aufrufende Funktion getWidthOfImage()
einen Rückruf registriert, und dann den Alert der Breite in diesen Rückruf verschieben.
function getWidthOfImage(src, cb) {
var img = document.createElement('img');
img.onload = function() {
cb(this.width);
};
img.src = src;
}
getWidthOfImage('lolcat.png', function (width) {
alert(width);
});
... wie zuvor, beachten Sie, dass wir die globalen Variablen (in diesem Fall width
) entfernen konnten .