Warum bleibt meine Variable unverändert, nachdem ich sie innerhalb einer Funktion geändert habe? - Asynchrone Code-Referenz

738

Warum ist anhand der folgenden Beispiele outerScopeVarin allen Fällen undefiniert?

var outerScopeVar;

var img = document.createElement('img');
img.onload = function() {
    outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);

var outerScopeVar;
setTimeout(function() {
    outerScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);

// Example using some jQuery
var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
});
alert(outerScopeVar);

// Node.js example
var outerScopeVar;
fs.readFile('./catdog.html', function(err, data) {
    outerScopeVar = data;
});
console.log(outerScopeVar);

// with promises
var outerScopeVar;
myPromise.then(function (response) {
    outerScopeVar = response;
});
console.log(outerScopeVar);

// geolocation API
var outerScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
    outerScopeVar = pos;
});
console.log(outerScopeVar);

Warum wird undefinedin all diesen Beispielen ausgegeben ? Ich möchte keine Problemumgehungen, ich möchte wissen, warum dies geschieht.


Hinweis: Dies ist eine kanonische Frage zur JavaScript-Asynchronität . Sie können diese Frage gerne verbessern und vereinfachte Beispiele hinzufügen, mit denen sich die Community identifizieren kann.

Fabrício Matté
quelle
@Dukeling danke, ich bin mir ziemlich sicher, dass ich mit diesem Link kommentiert habe, aber es fehlen anscheinend einige Kommentare. Auch in Bezug auf Ihre Bearbeitung: Ich glaube, dass "kanonisch" und "asynchron" im Titel hilfreich sind, wenn Sie nach dieser Frage suchen, um eine andere Frage als Betrogene zu markieren. Und natürlich hilft es auch, diese Frage von Google zu finden, wenn Sie nach Erklärungen zur Asynchronität suchen.
Fabrício Matté
5
Wenn man ein bisschen mehr darüber nachdenkt, ist "kanonisches Asynchronitätsthema" ein bisschen schwer im Titel, "asynchrone Code-Referenz" ist einfacher und objektiver. Ich glaube auch, dass die meisten Leute nach "asynchron" statt nach "Asynchronität" suchen.
Fabrício Matté
2
Einige Leute initialisieren ihre Variable vor dem Funktionsaufruf. Wie wäre es, den Titel zu ändern, der das auch irgendwie darstellt? Wie "Warum bleibt meine Variable unverändert, nachdem ich sie innerhalb einer Funktion geändert habe?" ?
Felix Kling
In allen Codebeispielen, die Sie oben erwähnt haben, "alert (äußereScopeVar);" wird JETZT ausgeführt, während die Zuweisung des Werts zu "OuterScopeVar" SPÄTER (asynchron) erfolgt.
Refactor

Antworten:

581

Ein Wort Antwort: Asynchronität .

Vorworte

Dieses Thema wurde hier in Stack Overflow mindestens ein paar tausend Mal wiederholt. Daher möchte ich zunächst auf einige äußerst nützliche Ressourcen hinweisen:


Die Antwort auf die vorliegende Frage

Lassen Sie uns zuerst das allgemeine Verhalten verfolgen. In allen Beispielen wird das outerScopeVarinnerhalb einer Funktion geändert . Diese Funktion wird eindeutig nicht sofort ausgeführt, sondern zugewiesen oder als Argument übergeben. Das nennen wir einen Rückruf .

Die Frage ist nun, wann dieser Rückruf aufgerufen wird.

Das hängt vom Fall ab. Versuchen wir noch einmal, ein häufiges Verhalten zu verfolgen:

  • img.onloadkann irgendwann in der Zukunft aufgerufen werden , wenn (und wenn) das Bild erfolgreich geladen wurde.
  • setTimeoutkann irgendwann in der Zukunft aufgerufen werden , nachdem die Verzögerung abgelaufen ist und das Timeout nicht von abgebrochen wurde clearTimeout. Hinweis: Selbst bei Verwendung 0als Verzögerung haben alle Browser eine minimale Zeitüberschreitungsverzögerung (in der HTML5-Spezifikation auf 4 ms festgelegt).
  • $.postDer Rückruf von jQuery wird möglicherweise irgendwann in der Zukunft aufgerufen , wenn (und wenn) die Ajax-Anforderung erfolgreich abgeschlossen wurde.
  • Node.js wird fs.readFilemöglicherweise irgendwann in der Zukunft aufgerufen , wenn die Datei erfolgreich gelesen wurde oder ein Fehler aufgetreten ist.

In allen Fällen haben wir einen Rückruf, der möglicherweise irgendwann in der Zukunft ausgeführt wird . Dies "irgendwann in der Zukunft" wird als asynchroner Fluss bezeichnet .

Die asynchrone Ausführung wird aus dem synchronen Fluss verdrängt. Das heißt, der asynchrone Code wird niemals ausgeführt, während der synchrone Codestapel ausgeführt wird. Dies ist die Bedeutung von JavaScript als Single-Thread.

Insbesondere wenn die JS-Engine inaktiv ist und keinen Stapel von (a) synchronem Code ausführt, werden Ereignisse abgefragt, die möglicherweise asynchrone Rückrufe ausgelöst haben (z. B. abgelaufenes Zeitlimit, empfangene Netzwerkantwort), und diese nacheinander ausgeführt. Dies wird als Ereignisschleife angesehen .

Das heißt, der in den handgezeichneten roten Formen hervorgehobene asynchrone Code kann erst ausgeführt werden, nachdem der gesamte verbleibende synchrone Code in den jeweiligen Codeblöcken ausgeführt wurde:

asynchroner Code hervorgehoben

Kurz gesagt, die Rückruffunktionen werden synchron erstellt, aber asynchron ausgeführt. Sie können sich nur dann auf die Ausführung einer asynchronen Funktion verlassen, wenn Sie wissen, dass sie ausgeführt wurde, und wie geht das?

Es ist wirklich einfach. Die Logik, die von der Ausführung der asynchronen Funktion abhängt, sollte innerhalb dieser asynchronen Funktion gestartet / aufgerufen werden. Wenn Sie beispielsweise alerts und console.logs auch innerhalb der Rückruffunktion verschieben, wird das erwartete Ergebnis ausgegeben, da das Ergebnis zu diesem Zeitpunkt verfügbar ist.

Implementierung Ihrer eigenen Rückruflogik

Oft müssen Sie mehr mit dem Ergebnis einer asynchronen Funktion tun oder verschiedene Dinge mit dem Ergebnis tun, je nachdem, wo die asynchrone Funktion aufgerufen wurde. Lassen Sie uns ein etwas komplexeres Beispiel angehen:

var outerScopeVar;
helloCatAsync();
alert(outerScopeVar);

function helloCatAsync() {
    setTimeout(function() {
        outerScopeVar = 'Nya';
    }, Math.random() * 2000);
}

Hinweis: Ich verwende setTimeoutmit einer zufälligen Verzögerung als eine allgemeine asynchrone Funktion gilt das gleiche Beispiel zu Ajax, readFile, onloadund anderer asynchroner Strömung.

Dieses Beispiel hat eindeutig das gleiche Problem wie die anderen Beispiele. Es wartet nicht, bis die asynchrone Funktion ausgeführt wird.

Lassen Sie uns ein eigenes Rückrufsystem implementieren. Zunächst einmal werden wir das Hässliche los, outerScopeVardas in diesem Fall völlig nutzlos ist. Dann fügen wir einen Parameter hinzu, der ein Funktionsargument akzeptiert, unseren Rückruf. Wenn die asynchrone Operation beendet ist, rufen wir diesen Rückruf auf und übergeben das Ergebnis. Die Implementierung (bitte lesen Sie die Kommentare in der Reihenfolge):

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    alert(result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as argument from the helloCatAsync call
function helloCatAsync(callback) {
    // 3. Start async operation:
    setTimeout(function() {
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

Codefragment des obigen Beispiels:

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
console.log("1. function called...")
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    console.log("5. result is: ", result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as argument from the helloCatAsync call
function helloCatAsync(callback) {
    console.log("2. callback here is the function passed as argument above...")
    // 3. Start async operation:
    setTimeout(function() {
    console.log("3. start async operation...")
    console.log("4. finished async operation, calling the callback, passing the result...")
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

In realen Anwendungsfällen stellen die DOM-API und die meisten Bibliotheken in den meisten Fällen bereits die Rückruffunktion bereit (die helloCatAsyncImplementierung in diesem demonstrativen Beispiel). Sie müssen nur die Rückruffunktion übergeben und verstehen, dass sie außerhalb des synchronen Flusses ausgeführt wird, und Ihren Code neu strukturieren, um dies zu berücksichtigen.

Sie werden auch feststellen, dass es aufgrund der asynchronen Natur unmöglich ist, returneinen Wert von einem asynchronen Fluss zurück zu dem synchronen Fluss zu erhalten, in dem der Rückruf definiert wurde, da die asynchronen Rückrufe lange ausgeführt werden, nachdem der synchrone Code bereits ausgeführt wurde.

Anstatt returneinen Wert aus einem asynchronen Rückruf zu verwenden, müssen Sie das Rückrufmuster verwenden oder ... Versprechen.

Versprechen

Obwohl es Möglichkeiten gibt, die Callback-Hölle mit Vanilla JS in Schach zu halten , werden Versprechen immer beliebter und werden derzeit in ES6 standardisiert (siehe Promise - MDN ).

Versprechen (auch bekannt als Futures) bieten ein lineareres und damit angenehmeres Lesen des asynchronen Codes, aber die Erklärung ihrer gesamten Funktionalität fällt nicht in den Geltungsbereich dieser Frage. Stattdessen überlasse ich diese hervorragenden Ressourcen den Interessierten:


Weitere Informationen zur JavaScript-Asynchronität

  • Die Kunst des Knotens - Rückrufe erklärt asynchronen Code und Rückrufe sehr gut mit Vanilla JS-Beispielen und Node.js-Code.

Hinweis: Ich habe diese Antwort als Community-Wiki markiert, daher kann jeder mit mindestens 100 Reputationen sie bearbeiten und verbessern! Bitte zögern Sie nicht, diese Antwort zu verbessern, oder senden Sie eine völlig neue Antwort, wenn Sie dies ebenfalls möchten.

Ich möchte diese Frage in ein kanonisches Thema verwandeln, um Asynchronitätsprobleme zu beantworten, die nichts mit Ajax zu tun haben (es gibt die Möglichkeit, die Antwort von einem AJAX-Aufruf zurückzugeben? ). Daher benötigt dieses Thema Ihre Hilfe, um so gut und hilfreich wie möglich zu sein !

Fabrício Matté
quelle
1
Gibt es in Ihrem letzten Beispiel einen bestimmten Grund, warum Sie anonyme Funktionen verwenden, oder würde es mit benannten Funktionen genauso funktionieren?
JDelage
1
Die Codebeispiele sind etwas seltsam, da Sie die Funktion nach dem Aufruf deklarieren. Funktioniert natürlich wegen des Hebens, aber war es beabsichtigt?
Bergi
2
ist es Deadlock. Felix Kling zeigt auf Ihre Antwort und Sie zeigen auf Felix Antwort
Mahi
1
Sie müssen verstehen, dass der Code des roten Kreises nur asynchron ist, da er von NATIVE asynchronen Javascript-Funktionen ausgeführt wird. Dies ist eine Funktion Ihrer Javascript-Engine - sei es Node.js oder ein Browser. Es ist asynchron, weil es als "Rückruf" an eine Funktion übergeben wird, die im Wesentlichen eine Black Box ist (implementiert in C usw.). Für den unglücklichen Entwickler sind sie asynchron ... nur weil. Wenn Sie Ihre eigene asynchrone Funktion schreiben möchten, müssen Sie sie hacken, indem Sie sie an SetTimeout (myfunc, 0) senden. Solltest du das tun? Noch eine Debatte ... wahrscheinlich nicht.
Sean Anderson
@Fabricio Ich habe nach der Spezifikation gesucht, die die "> = 4ms-Klemme" definiert, konnte sie aber nicht finden. Ich habe einige Erwähnungen eines ähnlichen Mechanismus (zum Klemmen verschachtelter Aufrufe) auf MDN gefunden - developer.mozilla.org/en-US/docs / Web / API /… - Hat jemand einen Link zum richtigen Teil der HTML-Spezifikation?
Sebi
156

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 verlief das Gespräch:

Me : Hallo Bob, ich muss wissen , wie wir foo ‚d der Bar ‘ letzte Woche d. Jim möchte einen Bericht darüber, und Sie sind 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!

Zu diesem Zeitpunkt legte ich auf. Da ich Informationen von Bob brauchte, um meinen Bericht zu vervollständigen, verließ ich den Bericht und ging stattdessen auf einen Kaffee, dann holte ich mir eine E-Mail. 40 Minuten später (Bob ist langsam) rief Bob zurück und gab mir die Informationen, die ich brauchte. Zu diesem Zeitpunkt 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 ‚d der Bar ‘ letzte Woche d. Jim möchte einen Bericht darüber, und Sie sind 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 asynchrones oder 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 alles langsame Vorgänge (im Kontext des modernen Rechnens).

Anstatt auf den Abschluss dieser langsamen Vorgänge zu warten , können Sie mit JavaScript eine Rückruffunktion registrieren, die ausgeführt wird, wenn der langsame Vorgang abgeschlossen ist. In der Zwischenzeit wird JavaScript jedoch weiterhin anderen Code ausführen. Die Tatsache, dass JavaScript anderen Code ausführt , während auf den langsamen Vorgang gewartet wird, macht das Verhalten asynchron . Hätte JavaScript gewartet, bis der Vorgang abgeschlossen war, 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 JavaScript zu laden lolcat.png, was eine langsame Operation ist. 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 das alert()sofort verarbeitet wird, anstatt nachdem das Bild geladen wurde.

Um unseren Code zu beheben, alles , was wir tun müssen , ist die bewegen alert(outerScopeVar)Code in der Callback - Funktion. Infolgedessen benötigen wir die outerScopeVarals 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 die einzige * Möglichkeit in JavaScript 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 können Sie auch verwenden eval(), ist aber für diesen Zweck eval()böse


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);

Jetzt wissen wir jedoch, dass return outerScopeVardies sofort geschieht. bevor die onloadRückruffunktion die Variable aktualisiert hat. Dies führt dazu, dass Sie getWidthOfImage()zurückkehren undefinedund undefinedalarmiert werden.

Um dies zu beheben, müssen wir dem Funktionsaufruf erlauben getWidthOfImage(), einen Rückruf zu registrieren, und dann die Warnung der Breite so verschieben, dass sie innerhalb dieses Rückrufs liegt.

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);
});

... beachten Sie nach wie vor, dass wir die globalen Variablen (in diesem Fall width) entfernen konnten .

Matt
quelle
4
Aber wie ist eine Warnung oder ein Senden an die Konsole nützlich, wenn Sie die Ergebnisse in einer anderen Berechnung verwenden oder in einer Objektvariablen speichern möchten?
Ken Ingram
73

Hier finden Sie eine präzisere Antwort für Personen, die nach einer Kurzreferenz suchen, sowie einige Beispiele, die Versprechen und Async / Warten verwenden.

Beginnen Sie mit dem naiven Ansatz (der nicht funktioniert) für eine Funktion, die eine asynchrone Methode aufruft (in diesem Fall setTimeout) und eine Nachricht zurückgibt:

function getMessage() {
  var outerScopeVar;
  setTimeout(function() {
    outerScopeVar = 'Hello asynchronous world!';
  }, 0);
  return outerScopeVar;
}
console.log(getMessage());

undefinedwird in diesem Fall protokolliert, da getMessagezurückgegeben wird, bevor der setTimeoutRückruf aufgerufen und aktualisiert wird outerScopeVar.

Die beiden Hauptlösungsmöglichkeiten sind Rückrufe und Versprechen :

Rückrufe

Die Änderung hier ist, dass getMessageein callbackParameter akzeptiert wird, der aufgerufen wird, um die Ergebnisse an den aufrufenden Code zurückzusenden, sobald dieser verfügbar ist.

function getMessage(callback) {
  setTimeout(function() {
    callback('Hello asynchronous world!');
  }, 0);
}
getMessage(function(message) {
  console.log(message);
});

Versprechen

Versprechen bieten eine Alternative, die flexibler ist als Rückrufe, da sie auf natürliche Weise kombiniert werden können, um mehrere asynchrone Vorgänge zu koordinieren. Eine Promises / A + -Standardimplementierung wird nativ in node.js (0.12+) und vielen aktuellen Browsern bereitgestellt, ist aber auch in Bibliotheken wie Bluebird und Q implementiert .

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello asynchronous world!');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);  
});

jQuery Deferreds

jQuery bietet Funktionen, die Versprechungen mit seinen Zurückgestellten ähneln.

function getMessage() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve('Hello asynchronous world!');
  }, 0);
  return deferred.promise();
}

getMessage().done(function(message) {
  console.log(message);  
});

async / warten

Wenn Ihre JavaScript-Umgebung Unterstützung für asyncund await(wie Node.js 7.6+) enthält, können Sie Versprechen synchron innerhalb von asyncFunktionen verwenden:

function getMessage () {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello asynchronous world!');
        }, 0);
    });
}

async function main() {
    let message = await getMessage();
    console.log(message);
}

main();
JohnnyHK
quelle
Ihr Beispiel für Versprechen ist im Grunde das, wonach ich in den letzten Stunden gesucht habe. Ihr Beispiel ist schön und erklärt gleichzeitig Versprechen. Warum dies nirgendwo anders ist, ist verwirrend.
Vincent P
Das ist alles in Ordnung, aber was ist, wenn Sie getMessage () mit Parametern aufrufen müssen? Wie würden Sie das Obige in diesem Szenario schreiben?
Chiwda
2
@Chiwda Sie setzen nur den Rückrufparameter zuletzt : function getMessage(param1, param2, callback) {...}.
JohnnyHK
Ich versuche Ihre async/awaitProbe, aber ich habe Probleme. Anstatt a zu instanziieren new Promise, .Get()rufe ich an und habe daher keinen Zugriff auf eine resolve()Methode. Somit gibt mein getMessage()das Versprechen zurück und nicht das Ergebnis. Könnten Sie Ihre Antwort ein wenig bearbeiten, um eine funktionierende Syntax dafür anzuzeigen?
InteXX
@InteXX Ich bin mir nicht sicher, was Sie mit einem .Get()Anruf meinen . Wahrscheinlich am besten eine neue Frage stellen.
JohnnyHK
56

Um das Offensichtliche auszudrücken, repräsentiert die Tasse outerScopeVar.

Asynchrone Funktionen sind wie ...

asynchroner Anruf für Kaffee

Johannes Fahrenkrug
quelle
14
Während der Versuch, eine asynchrone Funktion synchron zu machen, den Versuch bedeutet, den Kaffee nach 1 Sekunde zu trinken und ihn nach 1 Minute in Ihren Schoß zu gießen.
Teepeemm
Wenn es das Offensichtliche gesagt hätte, wäre die Frage wohl nicht gestellt worden: Nein?
Broccoli2000
2
@ broccoli2000 Damit meinte ich nicht, dass die Frage offensichtlich war, aber dass es offensichtlich ist, was die Tasse in der Zeichnung darstellt :)
Johannes Fahrenkrug
14

Die anderen Antworten sind ausgezeichnet und ich möchte nur eine direkte Antwort darauf geben. Nur auf asynchrone jQuery-Aufrufe beschränkt

Alle Ajax-Aufrufe (einschließlich $.getoder $.postoder $.ajax) sind asynchron.

Betrachten Sie Ihr Beispiel

var outerScopeVar;  //line 1
$.post('loldog', function(response) {  //line 2
    outerScopeVar = response;
});
alert(outerScopeVar);  //line 3

Die Codeausführung beginnt in Zeile 1, deklariert die Variable und löst einen asynchronen Aufruf in Zeile 2 (dh die Nachanforderung) aus und setzt ihre Ausführung in Zeile 3 fort, ohne darauf zu warten, dass die Nachanforderung ihre Ausführung abschließt.

Nehmen wir an, die Post-Anfrage dauert 10 Sekunden. Der Wert von outerScopeVarwird erst nach diesen 10 Sekunden festgelegt.

Ausprobieren,

var outerScopeVar; //line 1
$.post('loldog', function(response) {  //line 2, takes 10 seconds to complete
    outerScopeVar = response;
});
alert("Lets wait for some time here! Waiting is fun");  //line 3
alert(outerScopeVar);  //line 4

Wenn Sie dies jetzt ausführen, wird in Zeile 3 eine Warnung angezeigt. Warten Sie nun einige Zeit, bis Sie sicher sind, dass die Post-Anfrage einen Wert zurückgegeben hat. Wenn Sie dann im Warnfeld auf OK klicken, wird bei der nächsten Warnung der erwartete Wert gedruckt, da Sie darauf gewartet haben.

Im realen Szenario wird der Code,

var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
    alert(outerScopeVar);
});

Der gesamte Code, der von den asynchronen Aufrufen abhängt, wird innerhalb des asynchronen Blocks oder durch Warten auf die asynchronen Aufrufe verschoben.

Teja
quelle
or by waiting on the asynchronous callsWie macht man das?
InteXX
@InteXX Mit einer Rückrufmethode
Teja
Haben Sie ein kurzes Syntaxbeispiel?
InteXX
11

In all diesen Szenarien outerScopeVarwird ein Wert geändert oder asynchron zugewiesen oder tritt zu einem späteren Zeitpunkt auf (Warten oder Abhören eines Ereignisses), auf das die aktuelle Ausführung nicht wartet. In all diesen Fällen führt der aktuelle Ausführungsfluss zuouterScopeVar = undefined

Lassen Sie uns die einzelnen Beispiele diskutieren (ich habe den Teil markiert, der asynchron oder verzögert aufgerufen wird, damit einige Ereignisse auftreten):

1.

Geben Sie hier die Bildbeschreibung ein

Hier registrieren wir einen Ereignislisten, der bei diesem bestimmten Ereignis ausgeführt wird. Hier wird das Bild geladen. Dann wird die aktuelle Ausführung mit den nächsten Zeilen fortgesetzt, img.src = 'lolcat.png';und alert(outerScopeVar);währenddessen tritt das Ereignis möglicherweise nicht auf. Das heißt, die Funktion img.onloadwartet asynchron , bis das referenzierte Bild geladen ist. Dies geschieht im folgenden Beispiel - das Ereignis kann unterschiedlich sein.

2.

2

Hier spielt das Timeout-Ereignis die Rolle, die den Handler nach der angegebenen Zeit aufruft. Hier ist es 0, aber es registriert immer noch ein asynchrones Ereignis, das an die letzte Position der Event Queuezur Ausführung angefügt wird , wodurch die garantierte Verzögerung erreicht wird.

3.

Geben Sie hier die Bildbeschreibung ein Diesmal Ajax-Rückruf.

4.

Geben Sie hier die Bildbeschreibung ein

Der Knoten kann als König der asynchronen Codierung betrachtet werden. Hier wird die markierte Funktion als Rückruf-Handler registriert, der nach dem Lesen der angegebenen Datei ausgeführt wird.

5.

Geben Sie hier die Bildbeschreibung ein

Offensichtliches Versprechen (etwas wird in Zukunft getan) ist asynchron. Siehe Was sind die Unterschiede zwischen Zurückgestellt, Versprechen und Zukunft in JavaScript?

https://www.quora.com/Whats-the-difference-between-a-promise-and-a-callback-in-Javascript

Tom Sebastian
quelle