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

669

Warum ist outerScopeVarin den folgenden Beispielen in 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 es undefinedin all diesen Beispielen ausgegeben ? Ich möchte keine Problemumgehungen, ich möchte wissen, warum dies geschieht.


Hinweis: Dies ist eine kanonische Frage zur Asynchronität von JavaScript . Sie können diese Frage gerne verbessern und einfachere 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 hatte, aber es gibt anscheinend einige fehlende 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 Dupe zu markieren. Und natürlich hilft es auch, diese Frage von Google zu finden, wenn nach Erklärungen für Asynchronität gesucht wird.
Fabrício Matté
3
Wenn man ein bisschen mehr darüber nachdenkt, ist "Thema kanonische Asynchronität" ein bisschen schwerfällig im Titel, "asynchrone Codereferenz" ist einfacher und objektiver. Ich glaube auch, dass die meisten Leute nach "asynchron" anstatt nach "Asynchronität" suchen.
Fabrício Matté
1
Einige Leute initialisieren ihre Variable vor dem Funktionsaufruf. Wie wäre es, den Titel zu ändern, der das auch irgendwie repräsentiert? Wie "Warum bleibt meine Variable unverändert, nachdem ich sie in einer Funktion geändert habe?" ?
Felix Kling
In allen oben erwähnten Codebeispielen "alert (outerScopeVar);" wird JETZT ausgeführt, während die Zuweisung von Wert zu "outerScopeVar" SPÄTER (asynchron) erfolgt.
Refactor

Antworten:

542

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 Frage zur Hand

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

Nun ist die Frage, wann dieser Rückruf aufgerufen wird.

Es kommt auf den Fall an. 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 die Zeitüberschreitung von nicht abgebrochen wurde clearTimeout. Hinweis: Auch bei Verwendung 0als Verzögerung haben alle Browser eine minimale Zeitlimitüberschreitung (in der HTML5-Spezifikation auf 4 ms festgelegt).
  • $.postDer Rückruf von jQuery kann zu einem späteren Zeitpunkt aufgerufen werden , wenn (und wenn) die Ajax-Anforderung erfolgreich abgeschlossen wurde.
  • Node.js wird fs.readFilemöglicherweise zu einem späteren Zeitpunkt 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 ist "irgendwann in der Zukunft", was wir als asynchronen Fluss bezeichnen .

Die asynchrone Ausführung wird aus dem synchronen Datenfluss entfernt. Das heißt, der asynchrone Code wird niemals ausgeführt, während der synchrone Codestack ausgeführt wird. Dies ist die Bedeutung von Single-Threaded-JavaScript.

Wenn die JS-Engine im Leerlauf ist und keinen Stapel von (a) synchronem Code ausführt, sucht sie nach Ereignissen, die möglicherweise asynchrone Rückrufe ausgelöst haben (z. B. abgelaufenes Timeout, empfangene Netzwerkantwort), und führt sie nacheinander aus. Dies wird als Ereignisschleife angesehen .

Das heißt, der in den von Hand gezeichneten roten Formen hervorgehobene asynchrone Code wird möglicherweise erst ausgeführt, nachdem der gesamte verbleibende synchrone Code in den jeweiligen Codeblöcken ausgeführt wurde:

Async-Code markiert

Kurz gesagt, die Rückruffunktionen werden synchron erstellt, aber asynchron ausgeführt. Sie können sich erst 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 in dieser asynchronen Funktion gestartet / aufgerufen werden. Wenn Sie beispielsweise alerts und console.logs innerhalb der Rückruffunktion verschieben, wird das erwartete Ergebnis ausgegeben, da das Ergebnis zu diesem Zeitpunkt verfügbar ist.

Implementierung einer eigenen Callback-Logik

Je nachdem, wo die asynchrone Funktion aufgerufen wurde, müssen Sie häufig mehr mit dem Ergebnis einer asynchronen Funktion oder mit dem Ergebnis tun. Betrachten wir ein etwas komplexeres Beispiel:

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.

Nehmen wir uns vor, ein eigenes Rückrufsystem zu implementieren. Zuerst 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 Callback. 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);
}

Code-Snippet 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 den meisten realen Anwendungsfällen bieten die DOM-API und die meisten Bibliotheken bereits die Callback-Funktionalität (die helloCatAsyncImplementierung in diesem anschaulichen Beispiel). Sie müssen nur die Rückruffunktion übergeben und verstehen, dass sie außerhalb des synchronen Ablaufs ausgeführt wird, und Ihren Code neu strukturieren, um dies zu berücksichtigen.

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

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

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 Versprechen - MDN ).

Promises (auch als Futures bezeichnet) ermöglichen ein lineareres und damit angenehmeres Lesen des asynchronen Codes. Die Erklärung der gesamten Funktionalität ist jedoch nicht Gegenstand dieser Frage. Stattdessen überlasse ich den Interessierten diese hervorragenden Ressourcen:


Weiteres Lesematerial zu JavaScript-Asynchronität


Hinweis: Ich habe diese Antwort als Community-Wiki markiert, daher kann jeder mit mindestens 100 Ruf sie bearbeiten und verbessern! Sie können diese Antwort jederzeit verbessern oder eine völlig neue Antwort einreichen, wenn Sie dies ebenfalls möchten.

Ich möchte diese Frage in ein kanonisches Thema umwandeln, um Asynchronitätsprobleme zu beantworten, die nichts mit Ajax zu tun haben (dazu gibt es Wie kann ich die Antwort von einem AJAX-Aufruf zurückgeben? ). 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 dies bei Verwendung benannter 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 Sackgasse? Felix Kling zeigt auf Ihre Antwort und Sie zeigen auf Felix Antwort
Mahi
1
Sie müssen verstehen, dass der rote Kreis nur asynchron ist, da er von NATIVE asynchronen Javascript-Funktionen ausgeführt wird. Dies ist eine Funktion Ihres Javascript-Moduls - egal ob Node.js oder ein Browser. Es ist asynchron, weil es als "Rückruf" an eine Funktion übergeben wird, die im Wesentlichen eine Blackbox 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 machen Eine weitere Debatte .... wahrscheinlich nicht.
Sean Anderson
@Fabricio Ich habe nach der Spezifikation gesucht, in der die "> = 4ms clamp" definiert ist, konnte sie jedoch nicht finden. Auf MDN - developer.mozilla.org/en-US/docs wurde ein ähnlicher Mechanismus (zum Festhalten verschachtelter Aufrufe) erwähnt / Web / API /… - Hat jemand einen Link zum rechten Teil der HTML-Spezifikation?
Sebi
147

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 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 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 outerScopeVarsofort passiert; 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 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 .

Matt
quelle
Aber wie ist das Warnen oder Senden an die Konsole sinnvoll, wenn Sie die Ergebnisse in einer anderen Berechnung verwenden oder in einer Objektvariablen speichern möchten?
Ken Ingram
68

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

Beginnen Sie mit dem naiven Ansatz (der nicht funktioniert) für eine Funktion, die (in diesem Fall setTimeout) eine asynchrone Methode aufruft 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 getMessagevor dem setTimeoutAufruf des Rückrufs und der Aktualisierung zurückgegeben wird outerScopeVar.

Die beiden wichtigsten Lösungsansätze 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ückzugeben, 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 ist nativ in node.js (0.12+) und vielen aktuellen Browsern enthalten, wird 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- Zurückstellung

jQuery bietet Funktionen, die mit seinen Zurückgestellten den Versprechungen ä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 / erwarten

Wenn Ihre JavaScript-Umgebung Unterstützung für asyncund await(wie Node.js 7.6+) enthält, können Sie Versprechen innerhalb von asyncFunktionen synchron 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 Promises 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 oben genannte in diesem Szenario schreiben?
Chiwda
2
@Chiwda Sie den Rückruf setzen nur Parameter zuletzt: function getMessage(param1, param2, callback) {...}.
JohnnyHK
Ich versuche Ihre async/awaitProbe, aber ich stoße auf Probleme. Anstatt a zu instanziieren new Promise, .Get()rufe ich an und habe daher keinen Zugriff auf eine resolve()Methode. Also 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 zu zeigen?
InteXX
@InteXX Ich bin mir nicht sicher, was du mit einem .Get()Anruf meinst . Vermutlich am besten eine neue Frage posten.
JohnnyHK
52

Um das Offensichtliche auszudrücken, stellt die Tasse dar outerScopeVar.

Asynchrone Funktionen sind wie ...

asynchroner Anruf für Kaffee

Johannes Fahrenkrug
quelle
13
Während der Versuch, eine asynchrone Funktion synchron zu betreiben, der Versuch wäre, den Kaffee nach 1 Sekunde zu trinken und ihn nach 1 Minute auf den Schoß zu schütten.
Teepeemm,
Ich glaube nicht, dass die Frage gestellt worden wäre, nein?
Broccoli2000
2
@ broccoli2000 Damit meinte ich nicht, dass die Frage offensichtlich war, sondern dass es offensichtlich ist, was die Tasse in der Zeichnung darstellt :)
Johannes Fahrenkrug
13

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

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 aus (dh die Nachanforderung) und setzt die Ausführung in Zeile 3 fort, ohne auf den Abschluss der Nachanforderung zu warten.

Nehmen wir an, dass die Nachbearbeitungsanforderung 10 Sekunden dauert. 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 ausführen, wird in Zeile 3 eine Warnung angezeigt. Warten Sie nun einige Zeit, bis Sie sicher sind, dass die Post-Anfrage einen bestimmten Wert zurückgegeben hat. Wenn Sie dann im Warnungsfeld auf OK klicken, gibt die nächste Warnung den erwarteten Wert aus, 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
10

In all diesen Szenarien outerScopeVarwird ein Wert asynchron oder zu einem späteren Zeitpunkt (Warten oder Abwarten auf ein Ereignis) geändert oder zugewiesen , worauf 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.

Bildbeschreibung hier eingeben

Hier registrieren wir einen Ereignislistener, 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 in der alert(outerScopeVar);Zwischenzeit tritt das Ereignis möglicherweise nicht auf. Das heißt, die Funktion img.onloadwartet darauf, dass das betreffende Bild asynchron geladen wird. Dies wird in allen folgenden Beispielen geschehen - das Ereignis kann davon abweichen.

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 hinzuaddiert wird, was die garantierte Verzögerung bewirkt.

3.

Bildbeschreibung hier eingeben Diesmal Ajax Callback.

4.

Bildbeschreibung hier eingeben

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

5.

Bildbeschreibung hier eingeben

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

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

Tom Sebastian
quelle