→ Eine allgemeinere Erklärung des asynchronen Verhaltens anhand verschiedener Beispiele finden Sie unter Warum bleibt meine Variable unverändert, nachdem ich sie innerhalb einer Funktion geändert habe? - Asynchrone Code-Referenz
→ Wenn Sie das Problem bereits verstanden haben, fahren Sie mit den folgenden möglichen Lösungen fort.
Das Problem
Das A in Ajax steht für asynchron . Das bedeutet, dass das Senden der Anforderung (oder vielmehr das Empfangen der Antwort) aus dem normalen Ausführungsfluss herausgenommen wird. In Ihrem Beispiel wird $.ajax
sofort zurückgegeben und die nächste Anweisung return result;
wird ausgeführt, bevor die Funktion, die Sie als success
Rückruf übergeben haben, überhaupt aufgerufen wurde.
Hier ist eine Analogie, die hoffentlich den Unterschied zwischen synchronem und asynchronem Fluss klarer macht:
Synchron
Stellen Sie sich vor, Sie telefonieren mit einem Freund und bitten ihn, etwas für Sie nachzuschlagen. Obwohl es eine Weile dauern kann, warten Sie am Telefon und starren in den Weltraum, bis Ihr Freund Ihnen die Antwort gibt, die Sie benötigen.
Das gleiche passiert, wenn Sie einen Funktionsaufruf mit "normalem" Code ausführen:
function findItem() {
var item;
while(item_not_found) {
// search
}
return item;
}
var item = findItem();
// Do something with item
doSomethingElse();
Auch wenn findItem
eine lange Zeit ausführen dauern kann, kommt jeder Code nach var item = findItem();
muss warten , bis die Funktion das Ergebnis zurück.
Asynchron
Sie rufen Ihren Freund aus dem gleichen Grund erneut an. Aber diesmal sagst du ihm, dass du es eilig hast und er dich auf deinem Handy zurückrufen sollte. Sie legen auf, verlassen das Haus und tun, was Sie vorhatten. Sobald Ihr Freund Sie zurückruft, haben Sie es mit den Informationen zu tun, die er Ihnen gegeben hat.
Genau das passiert, wenn Sie eine Ajax-Anfrage stellen.
findItem(function(item) {
// Do something with item
});
doSomethingElse();
Anstatt auf die Antwort zu warten, wird die Ausführung sofort fortgesetzt und die Anweisung nach dem Ajax-Aufruf ausgeführt. Um die Antwort schließlich zu erhalten, geben Sie eine Funktion an, die aufgerufen werden kann, sobald die Antwort empfangen wurde, einen Rückruf (beachten Sie etwas? Rückruf ?). Jede Anweisung, die nach diesem Aufruf kommt, wird ausgeführt, bevor der Rückruf aufgerufen wird.
Lösung (en)
Umfassen Sie die asynchrone Natur von JavaScript! Während bestimmte asynchrone Operationen synchrone Gegenstücke bereitstellen (ebenso wie "Ajax"), wird generell davon abgeraten, diese zu verwenden, insbesondere in einem Browserkontext.
Warum ist es schlecht, fragst du?
JavaScript wird im UI-Thread des Browsers ausgeführt, und bei einem lang laufenden Prozess wird die Benutzeroberfläche gesperrt, sodass sie nicht mehr reagiert. Darüber hinaus gibt es eine Obergrenze für die Ausführungszeit von JavaScript, und der Browser fragt den Benutzer, ob die Ausführung fortgesetzt werden soll oder nicht.
All dies ist eine wirklich schlechte Benutzererfahrung. Der Benutzer kann nicht feststellen, ob alles einwandfrei funktioniert oder nicht. Darüber hinaus ist der Effekt für Benutzer mit einer langsamen Verbindung schlechter.
Im Folgenden werden drei verschiedene Lösungen betrachtet, die alle aufeinander aufbauen:
- Versprechen mit
async/await
(ES2017 +, verfügbar in älteren Browsern, wenn Sie einen Transpiler oder Regenerator verwenden)
- Rückrufe (beliebt im Knoten)
- Versprechen mit
then()
(ES2015 +, verfügbar in älteren Browsern, wenn Sie eine der vielen Versprechen-Bibliotheken verwenden)
Alle drei sind in aktuellen Browsern und Knoten 7+ verfügbar.
ES2017 +: Versprechen mit async/await
Mit der 2017 veröffentlichten ECMAScript-Version wurde die Unterstützung für asynchrone Funktionen auf Syntaxebene eingeführt. Mit Hilfe von async
und await
können Sie asynchron in einem "synchronen Stil" schreiben. Der Code ist immer noch asynchron, aber leichter zu lesen / verstehen.
async/await
baut auf Versprechungen auf: Eine async
Funktion gibt immer ein Versprechen zurück. await
"packt" ein Versprechen aus und führt entweder zu dem Wert, mit dem das Versprechen gelöst wurde, oder wirft einen Fehler aus, wenn das Versprechen abgelehnt wurde.
Wichtig: Sie können nur await
innerhalb einer async
Funktion verwenden. await
Derzeit wird die oberste Ebene noch nicht unterstützt. Daher müssen Sie möglicherweise ein asynchrones IIFE ( sofort aufgerufener Funktionsausdruck ) erstellen , um einen async
Kontext zu starten .
Sie können mehr über async
und await
auf MDN lesen .
Hier ist ein Beispiel, das auf der obigen Verzögerung aufbaut:
// Using 'superagent' which will return a promise.
var superagent = require('superagent')
// This is isn't declared as `async` because it already returns a promise
function delay() {
// `delay` returns a promise
return new Promise(function(resolve, reject) {
// Only `delay` is able to resolve or reject the promise
setTimeout(function() {
resolve(42); // After 3 seconds, resolve the promise with value 42
}, 3000);
});
}
async function getAllBooks() {
try {
// GET a list of book IDs of the current user
var bookIDs = await superagent.get('/user/books');
// wait for 3 seconds (just for the sake of this example)
await delay();
// GET information about each book
return await superagent.get('/books/ids='+JSON.stringify(bookIDs));
} catch(error) {
// If any of the awaited promises was rejected, this catch block
// would catch the rejection reason
return null;
}
}
// Start an IIFE to use `await` at the top level
(async function(){
let books = await getAllBooks();
console.log(books);
})();
Aktuelle Browser- und Knotenversionen unterstützen async/await
. Sie können auch ältere Umgebungen unterstützen, indem Sie Ihren Code mithilfe von Regenerator (oder Tools, die Regenerator verwenden, wie z. B. Babel ) in ES5 umwandeln .
Lassen Sie Funktionen Rückrufe akzeptieren
Ein Rückruf ist einfach eine Funktion, die an eine andere Funktion übergeben wird. Diese andere Funktion kann die übergebene Funktion aufrufen, wann immer sie bereit ist. Im Kontext eines asynchronen Prozesses wird der Rückruf immer dann aufgerufen, wenn der asynchrone Prozess ausgeführt wird. Normalerweise wird das Ergebnis an den Rückruf übergeben.
Im Beispiel der Frage können Sie foo
einen Rückruf annehmen und als success
Rückruf verwenden. Also das
var result = foo();
// Code that depends on 'result'
wird
foo(function(result) {
// Code that depends on 'result'
});
Hier haben wir die Funktion "inline" definiert, aber Sie können jede Funktionsreferenz übergeben:
function myCallback(result) {
// Code that depends on 'result'
}
foo(myCallback);
foo
selbst ist wie folgt definiert:
function foo(callback) {
$.ajax({
// ...
success: callback
});
}
callback
wird sich auf die Funktion beziehen, an die wir übergeben, foo
wenn wir sie aufrufen, und wir geben sie einfach an weiter success
. Dh wenn die Ajax-Anfrage erfolgreich ist, $.ajax
wird callback
die Antwort aufgerufen und an den Rückruf weitergeleitet (auf den verwiesen werden kann result
, da wir den Rückruf auf diese Weise definiert haben).
Sie können die Antwort auch verarbeiten, bevor Sie sie an den Rückruf weiterleiten:
function foo(callback) {
$.ajax({
// ...
success: function(response) {
// For example, filter the response
callback(filtered_response);
}
});
}
Es ist einfacher, Code mithilfe von Rückrufen zu schreiben, als es scheint. Schließlich ist JavaScript im Browser stark ereignisgesteuert (DOM-Ereignisse). Das Empfangen der Ajax-Antwort ist nichts anderes als ein Ereignis.
Wenn Sie mit Code von Drittanbietern arbeiten müssen, können Schwierigkeiten auftreten. Die meisten Probleme können jedoch gelöst werden, indem Sie nur den Anwendungsfluss durchdenken.
ES2015 +: Verspricht mit then ()
Die Promise-API ist eine neue Funktion von ECMAScript 6 (ES2015), bietet jedoch bereits eine gute Browserunterstützung . Es gibt auch viele Bibliotheken, die die Standard-Promises-API implementieren und zusätzliche Methoden bereitstellen, um die Verwendung und Zusammensetzung von asynchronen Funktionen (z . B. Bluebird ) zu vereinfachen .
Versprechen sind Container für zukünftige Werte. Wenn das Versprechen den Wert erhält (es wird aufgelöst ) oder wenn es storniert ( abgelehnt ) wird, benachrichtigt es alle "Listener", die auf diesen Wert zugreifen möchten.
Der Vorteil gegenüber einfachen Rückrufen besteht darin, dass Sie Ihren Code entkoppeln können und einfacher zu erstellen sind.
Hier ist ein einfaches Beispiel für die Verwendung eines Versprechens:
function delay() {
// `delay` returns a promise
return new Promise(function(resolve, reject) {
// Only `delay` is able to resolve or reject the promise
setTimeout(function() {
resolve(42); // After 3 seconds, resolve the promise with value 42
}, 3000);
});
}
delay()
.then(function(v) { // `delay` returns a promise
console.log(v); // Log the value once it is resolved
})
.catch(function(v) {
// Or do something else if it is rejected
// (it would not happen in this example, since `reject` is not called).
});
Auf unseren Ajax-Aufruf angewendet könnten wir solche Versprechen verwenden:
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open('GET', url);
xhr.send();
});
}
ajax("/echo/json")
.then(function(result) {
// Code depending on result
})
.catch(function() {
// An error occurred
});
Die Beschreibung aller Vorteile, die das Versprechen bietet, würde den Rahmen dieser Antwort sprengen. Wenn Sie jedoch neuen Code schreiben, sollten Sie diese ernsthaft in Betracht ziehen. Sie bieten eine großartige Abstraktion und Trennung Ihres Codes.
Weitere Informationen zu Versprechungen: HTML5 rockt - JavaScript-Versprechungen
Randnotiz: Zurückgestellte Objekte von jQuery
Zurückgestellte Objekte sind die benutzerdefinierte Implementierung von Versprechen durch jQuery (bevor die Promise-API standardisiert wurde). Sie verhalten sich fast wie Versprechen, zeigen jedoch eine etwas andere API.
Jede Ajax-Methode von jQuery gibt bereits ein "zurückgestelltes Objekt" zurück (eigentlich ein Versprechen eines zurückgestellten Objekts), das Sie einfach von Ihrer Funktion zurückgeben können:
function ajax() {
return $.ajax(...);
}
ajax().done(function(result) {
// Code depending on result
}).fail(function() {
// An error occurred
});
Randnotiz: Versprechen Fallstricke
Denken Sie daran, dass Versprechen und zurückgestellte Objekte nur Container für einen zukünftigen Wert sind, sie sind nicht der Wert selbst. Angenommen, Sie hatten Folgendes:
function checkPassword() {
return $.ajax({
url: '/password',
data: {
username: $('#username').val(),
password: $('#password').val()
},
type: 'POST',
dataType: 'json'
});
}
if (checkPassword()) {
// Tell the user they're logged in
}
Dieser Code versteht die oben genannten Asynchronitätsprobleme falsch. Insbesondere wird $.ajax()
der Code nicht eingefroren, während die Seite '/ password' auf Ihrem Server überprüft wird. Er sendet eine Anforderung an den Server und gibt während des Wartens sofort ein jQuery Ajax Deferred-Objekt zurück, nicht die Antwort vom Server. Das bedeutet, dass die if
Anweisung dieses verzögerte Objekt immer abruft, als behandelt true
und so fortfährt, als wäre der Benutzer angemeldet. Nicht gut.
Aber die Lösung ist einfach:
checkPassword()
.done(function(r) {
if (r) {
// Tell the user they're logged in
} else {
// Tell the user their password was bad
}
})
.fail(function(x) {
// Tell the user something bad happened
});
Nicht empfohlen: Synchrone "Ajax" -Aufrufe
Wie bereits erwähnt, haben einige (!) Asynchrone Operationen synchrone Gegenstücke. Ich befürworte ihre Verwendung nicht, aber der Vollständigkeit halber würden Sie hier einen synchronen Anruf ausführen:
Ohne jQuery
Wenn Sie ein XMLHTTPRequest
Objekt direkt verwenden , übergeben Sie es false
als drittes Argument an .open
.
jQuery
Wenn Sie jQuery verwenden , können Sie die async
Option auf setzen false
. Beachten Sie, dass diese Option seit jQuery 1.8 veraltet ist. Sie können dann entweder noch einen success
Rückruf verwenden oder auf die responseText
Eigenschaft des jqXHR-Objekts zugreifen :
function foo() {
var jqXHR = $.ajax({
//...
async: false
});
return jqXHR.responseText;
}
Wenn Sie eine andere jQuery Ajax - Methode verwenden, wie zum Beispiel $.get
, $.getJSON
usw., müssen Sie es ändern $.ajax
(da Sie nur Konfigurationsparameter passieren können $.ajax
).
Kopf hoch! Es ist nicht möglich, eine synchrone JSONP- Anfrage zu stellen. JSONP ist von Natur aus immer asynchron (ein weiterer Grund, diese Option nicht einmal in Betracht zu ziehen).
If you use any other jQuery AJAX method, such as $.get, $.getJSON, etc., you have them to $.ajax.
(Ja, mir ist klar, dass mein Nick in diesem Fall ein bisschen ironisch ist)foo
aufgerufen wird und eine Funktion an ihn übergeben wird (foo(function(result) {....});
)?result
wird in dieser Funktion verwendet und ist die Antwort der Ajax-Anfrage. Um auf diese Funktion zu verweisen, wird anstelle einer anonymen Funktion der erste Parameter von foo aufgerufencallback
und zugewiesensuccess
. Also,$.ajax
wird anrufen,callback
wenn die Anfrage erfolgreich war. Ich habe versucht, es ein bisschen mehr zu erklären.$.getJSON
wenn die Ajax-Anforderung synchron sein soll. Sie sollten jedoch nicht möchten, dass die Anforderung synchron ist, damit dies nicht zutrifft. Sie sollten Rückrufe oder Versprechen verwenden, um die Antwort zu verarbeiten, wie weiter oben in der Antwort erläutert.Wenn Sie nicht jQuery in Ihrem Code verwenden, ist diese Antwort für Sie
Ihr Code sollte ungefähr so aussehen:
Felix Kling hat gute Arbeit geleistet und eine Antwort für Leute geschrieben, die jQuery für AJAX verwenden. Ich habe beschlossen, eine Alternative für Leute bereitzustellen, die es nicht sind.
( Hinweis: Für diejenigen, die die neue
fetch
API, Angular oder Versprechen verwenden, habe ich unten eine weitere Antwort hinzugefügt. )Was du vor dir hast
Dies ist eine kurze Zusammenfassung von "Erklärung des Problems" aus der anderen Antwort. Wenn Sie sich nach dem Lesen nicht sicher sind, lesen Sie diese.
Das A in AJAX steht für asynchron . Das bedeutet, dass das Senden der Anforderung (oder vielmehr das Empfangen der Antwort) aus dem normalen Ausführungsfluss herausgenommen wird. In Ihrem Beispiel wird
.send
sofort zurückgegeben und die nächste Anweisungreturn result;
wird ausgeführt, bevor die Funktion, die Sie alssuccess
Rückruf übergeben haben, überhaupt aufgerufen wurde.Dies bedeutet, dass der von Ihnen definierte Listener bei Ihrer Rückkehr noch nicht ausgeführt wurde. Dies bedeutet, dass der von Ihnen zurückgegebene Wert nicht definiert wurde.
Hier ist eine einfache Analogie
(Geige)
Der Wert von
a
return ist,undefined
da dasa=5
Teil noch nicht ausgeführt wurde. AJAX verhält sich so: Sie geben den Wert zurück, bevor der Server Ihrem Browser mitteilen kann, um welchen Wert es sich handelt.Eine mögliche Lösung für dieses Problem ist es , Code wieder aktiv , erzählen Sie Ihr Programm , was zu tun , wenn die Berechnung abgeschlossen.
Dies wird als CPS bezeichnet . Grundsätzlich übergeben wir
getFive
eine Aktion, die ausgeführt werden soll, wenn sie abgeschlossen ist. Wir teilen unserem Code mit, wie er reagieren soll, wenn ein Ereignis abgeschlossen ist (wie unser AJAX-Aufruf oder in diesem Fall das Timeout).Verwendung wäre:
Welches sollte "5" auf dem Bildschirm alarmieren. (Geige) .
Mögliche Lösungen
Grundsätzlich gibt es zwei Möglichkeiten, dies zu lösen:
1. Synchroner AJAX - Tu es nicht !!
Was synchrones AJAX betrifft, tun Sie es nicht! Felix 'Antwort wirft einige überzeugende Argumente dafür auf, warum es eine schlechte Idee ist. Zusammenfassend lässt sich sagen, dass der Browser des Benutzers eingefroren wird, bis der Server die Antwort zurückgibt und eine sehr schlechte Benutzererfahrung erzeugt. Hier ist eine weitere kurze Zusammenfassung von MDN, warum:
Wenn Sie es tun müssen, können Sie eine Flagge übergeben: So geht's:
2. Code umstrukturieren
Lassen Sie Ihre Funktion einen Rückruf annehmen. Im Beispiel
foo
kann Code verwendet werden, um einen Rückruf anzunehmen. Wir werden unseren Code erzählen , wie man reagieren , wennfoo
abgeschlossen ist .Damit:
Wird:
Hier haben wir eine anonyme Funktion übergeben, aber wir könnten genauso gut einen Verweis auf eine vorhandene Funktion übergeben, sodass es so aussieht:
Weitere Informationen dazu, wie diese Art von Rückrufdesign ausgeführt wird, finden Sie in Felix 'Antwort.
Definieren wir nun foo selbst, um entsprechend zu handeln
(Geige)
Wir haben jetzt unsere foo-Funktion dazu gebracht, eine Aktion zu akzeptieren, die ausgeführt werden soll, wenn der AJAX erfolgreich abgeschlossen wurde. Wir können dies weiter erweitern, indem wir prüfen, ob der Antwortstatus nicht 200 ist, und entsprechend handeln (einen Fail-Handler erstellen und so weiter). Wir lösen unser Problem effektiv.
Wenn Sie immer noch Schwierigkeiten haben, dies zu verstehen, lesen Sie den AJAX-Leitfaden für die ersten Schritte bei MDN.
quelle
onload
Handlers, der nur dann ausgelöst wird, wenn dies der FallreadyState
ist4
. Natürlich wird es in IE8 nicht unterstützt. (iirc, muss möglicherweise bestätigt werden.)XMLHttpRequest 2 (lesen Sie zuerst die Antworten von Benjamin Gruenbaum & Felix Kling )
Wenn Sie jQuery nicht verwenden und eine schöne kurze XMLHttpRequest 2 möchten, die auf den modernen Browsern und auch auf den mobilen Browsern funktioniert, empfehle ich, sie folgendermaßen zu verwenden:
Wie du siehst:
Es gibt zwei Möglichkeiten, um die Antwort auf diesen Ajax-Aufruf zu erhalten (drei mit dem Variablennamen XMLHttpRequest):
Das einfachste:
Oder wenn Sie aus irgendeinem Grund
bind()
den Rückruf zu einer Klasse:Beispiel:
Oder (die obige ist besser, anonyme Funktionen sind immer ein Problem):
Nichts einfacher.
Jetzt werden einige Leute wahrscheinlich sagen, dass es besser ist, onreadystatechange oder sogar den Variablennamen XMLHttpRequest zu verwenden. Das ist falsch.
Schauen Sie sich die erweiterten Funktionen von XMLHttpRequest an
Es werden alle * modernen Browser unterstützt. Und ich kann bestätigen, dass ich diesen Ansatz verwende, da XMLHttpRequest 2 vorhanden ist. Ich hatte nie Probleme mit allen von mir verwendeten Browsern.
onreadystatechange ist nur nützlich, wenn Sie die Header in Status 2 erhalten möchten.
Die Verwendung des
XMLHttpRequest
Variablennamens ist ein weiterer großer Fehler, da Sie den Rückruf innerhalb der Onload / oreadystatechange-Verschlüsse ausführen müssen, sonst haben Sie ihn verloren.Wenn Sie nun mit post und FormData etwas Komplexeres möchten, können Sie diese Funktion ganz einfach erweitern:
Wieder ... es ist eine sehr kurze Funktion, aber es wird & postet.
Anwendungsbeispiele:
Oder übergeben Sie ein vollständiges Formularelement (
document.getElementsByTagName('form')[0]
):Oder legen Sie einige benutzerdefinierte Werte fest:
Wie Sie sehen, habe ich die Synchronisierung nicht implementiert ... es ist eine schlechte Sache.
Trotzdem ... warum nicht einfach?
Wie im Kommentar erwähnt, bricht die Verwendung von error && synchronous den Punkt der Antwort vollständig. Welches ist eine schöne kurze Möglichkeit, Ajax richtig einzusetzen?
Fehlerbehandlungsroutine
Im obigen Skript haben Sie eine Fehlerbehandlungsroutine, die statisch definiert ist, damit die Funktion nicht beeinträchtigt wird. Der Fehlerbehandler kann auch für andere Funktionen verwendet werden.
Um einen Fehler wirklich zu beheben, müssen Sie nur eine falsche URL schreiben. In diesem Fall gibt jeder Browser einen Fehler aus.
Fehlerbehandlungsroutinen sind möglicherweise nützlich, wenn Sie benutzerdefinierte Header festlegen, den responseType auf Blob-Array-Puffer setzen oder was auch immer ...
Selbst wenn Sie 'POSTAPAPAP' als Methode übergeben, wird kein Fehler ausgegeben.
Selbst wenn Sie 'fdggdgilfdghfldj' als Formdaten übergeben, wird kein Fehler ausgegeben.
Im ersten Fall ist der Fehler in der
displayAjax()
unterthis.statusText
wieMethod not Allowed
.Im zweiten Fall funktioniert es einfach. Sie müssen auf der Serverseite prüfen, ob Sie die richtigen Postdaten übergeben haben.
domänenübergreifend nicht erlaubt löst automatisch einen Fehler aus.
In der Fehlerantwort gibt es keine Fehlercodes.
Es gibt nur das,
this.type
was auf Fehler gesetzt ist.Warum einen Fehlerbehandler hinzufügen, wenn Sie keine Kontrolle über Fehler haben? Die meisten Fehler werden in der Rückruffunktion zurückgegeben
displayAjax()
.Also: Keine Notwendigkeit für Fehlerprüfungen, wenn Sie die URL richtig kopieren und einfügen können. ;)
PS: Als ersten Test habe ich x ('x', displayAjax) geschrieben ... und es hat total eine Antwort bekommen ... ??? Also habe ich den Ordner überprüft, in dem sich der HTML-Code befindet, und es gab eine Datei namens 'x.xml'. Selbst wenn Sie die Erweiterung Ihrer Datei vergessen, wird XMLHttpRequest 2 sie finden . Ich habe laut gelacht
Lesen Sie eine Datei synchron
Tu das nicht.
Wenn Sie den Browser für eine Weile blockieren möchten, laden Sie eine schöne große
.txt
Datei synchron.Jetzt können Sie tun
Es gibt keine andere Möglichkeit, dies nicht asynchron zu tun. (Ja, mit setTimeout-Schleife ... aber im Ernst?)
Ein weiterer Punkt ist ... wenn Sie mit APIs oder nur den Dateien Ihrer eigenen Liste arbeiten oder was auch immer Sie immer unterschiedliche Funktionen für jede Anfrage verwenden ...
Nur wenn Sie eine Seite haben, auf der Sie immer das gleiche XML / JSON laden oder was auch immer Sie nur eine Funktion benötigen. Ändern Sie in diesem Fall die Ajax-Funktion ein wenig und ersetzen Sie b durch Ihre spezielle Funktion.
Die oben genannten Funktionen dienen der grundlegenden Verwendung.
Wenn Sie die Funktion erweitern möchten ...
Ja, du kannst.
Ich verwende viele APIs und eine der ersten Funktionen, die ich in jede HTML-Seite integriere, ist die erste Ajax-Funktion in dieser Antwort, nur mit GET ...
Mit XMLHttpRequest 2 können Sie jedoch eine Menge Dinge tun:
Ich habe einen Download-Manager (mit Bereichen auf beiden Seiten mit Resume, Filereader, Dateisystem) erstellt, verschiedene Image Resizer-Konverter mit Canvas, Web-SQL-Datenbanken mit base64images und vielem mehr ... Aber in diesen Fällen sollten Sie nur dafür eine Funktion erstellen Zweck ... manchmal benötigen Sie einen Blob, Array-Puffer, Sie können Header setzen, Mimetyp überschreiben und es gibt noch viel mehr ...
Aber die Frage hier ist, wie man eine Ajax-Antwort zurückgibt ... (Ich habe einen einfachen Weg hinzugefügt.)
quelle
x
,ajax
oderxhr
könnte schöner sein :)). Ich sehe nicht, wie es die Rückgabe der Antwort von einem AJAX-Anruf adressiert. (Jemand könnte es immer noch tunvar res = x("url")
und nicht verstehen, warum es nicht funktioniert;)). Nebenbei bemerkt - es wäre cool, wenn Siec
von der Methode zurückkehren würden, damit Benutzer sich anschließen könnenerror
usw.2.ajax is meant to be async.. so NO var res=x('url')..
Das ist der ganze Punkt dieser Frage und Antworten :)Wenn Sie Versprechen verwenden, ist diese Antwort für Sie.
Dies bedeutet AngularJS, jQuery (mit verzögertem Zugriff), das Ersetzen (Abrufen) von nativem XHR, EmberJS, das Speichern von BackboneJS oder jede Knotenbibliothek, die Versprechen zurückgibt.
Ihr Code sollte ungefähr so aussehen:
Felix Kling hat gute Arbeit geleistet und eine Antwort für Leute geschrieben, die jQuery mit Rückrufen für AJAX verwenden. Ich habe eine Antwort für native XHR. Diese Antwort gilt für die generische Verwendung von Versprechungen im Frontend oder Backend.
Das Kernthema
Das JavaScript-Parallelitätsmodell im Browser und auf dem Server mit NodeJS / io.js ist asynchron und reaktiv .
Wenn Sie eine Methode aufrufen, die ein Versprechen zurückgibt, werden die
then
Handler immer asynchron ausgeführt, dh nach dem Code unter ihnen, der sich nicht in einem.then
Handler befindet.Dies bedeutet, dass
data
der vonthen
Ihnen definierte Handler bei der Rückgabe noch nicht ausgeführt wurde. Dies bedeutet wiederum, dass der von Ihnen zurückgegebene Wert nicht rechtzeitig auf den richtigen Wert eingestellt wurde.Hier ist eine einfache Analogie für das Problem:
Der Wert von
data
ist,undefined
da dasdata = 5
Teil noch nicht ausgeführt wurde. Es wird wahrscheinlich in einer Sekunde ausgeführt, ist aber zu diesem Zeitpunkt für den zurückgegebenen Wert irrelevant.Da der Vorgang noch nicht ausgeführt wurde (AJAX, Serveraufruf, E / A, Timer), geben Sie den Wert zurück, bevor die Anforderung die Möglichkeit erhielt, Ihrem Code den Wert mitzuteilen.
Eine mögliche Lösung für dieses Problem ist es , Code wieder aktiv , erzählen Sie Ihr Programm , was zu tun , wenn die Berechnung abgeschlossen. Versprechen ermöglichen dies aktiv, indem sie zeitlicher (zeitkritischer) Natur sind.
Schnelle Zusammenfassung der Versprechen
Ein Versprechen ist ein Wert im Laufe der Zeit . Versprechen haben Status, sie beginnen als ausstehend ohne Wert und können sich mit Folgendem abfinden:
Ein Versprechen kann den Zustand nur einmal ändern, danach bleibt es für immer im selben Zustand. Sie können
then
Handler an Versprechen anhängen , um deren Wert zu extrahieren und Fehler zu behandeln.then
Handler ermöglichen die Verkettung von Anrufen. Versprechen werden mithilfe von APIs erstellt, die sie zurückgeben . Zum Beispiel der modernere AJAX-Ersatzfetch
oder die$.get
Rückkehr von jQuery .Wenn wir rufen
.then
auf ein Versprechen und Rückkehr etwas von ihm - wir ein Versprechen für bekommen den verarbeiteten Wert . Wenn wir ein weiteres Versprechen zurückgeben, werden wir erstaunliche Dinge bekommen, aber lassen Sie uns unsere Pferde halten.Mit Versprechungen
Mal sehen, wie wir das oben genannte Problem mit Versprechungen lösen können. Lassen Sie uns zunächst unser Verständnis der Versprechen von oben demonstrieren, indem wir den Versprechen-Konstruktor zum Erstellen einer Verzögerungsfunktion verwenden:
Nachdem wir setTimeout so konvertiert haben, dass es Versprechen verwendet, können wir es verwenden
then
, damit es zählt:Grundsätzlich statt einer Rückkehr Wert , den wir nicht wegen der Gleichzeitigkeit Modell tun - wir Rückkehr einen Wrapper für einen Wert , dass wir auspacken mit
then
. Es ist wie eine Schachtel, mit der man öffnen kannthen
.Dies anwenden
Dies gilt auch für Ihren ursprünglichen API-Aufruf. Sie können:
Das funktioniert also genauso gut. Wir haben gelernt, dass wir keine Werte von bereits asynchronen Aufrufen zurückgeben können, aber wir können Versprechen verwenden und sie verketten, um die Verarbeitung durchzuführen. Wir wissen jetzt, wie die Antwort von einem asynchronen Aufruf zurückgegeben wird.
ES2015 (ES6)
ES6 führt Generatoren ein , Funktionen, die in der Mitte zurückkehren und dann den Punkt wieder aufnehmen können, an dem sie sich befanden. Dies ist normalerweise nützlich für Sequenzen, zum Beispiel:
Ist eine Funktion, die einen Iterator über die Sequenz zurückgibt
1,2,3,3,3,3,....
, der iteriert werden kann. Während dies für sich genommen interessant ist und Raum für viele Möglichkeiten eröffnet, gibt es einen besonders interessanten Fall.Wenn es sich bei der von uns erstellten Sequenz eher um eine Sequenz von Aktionen als um Zahlen handelt, können wir die Funktion anhalten, wenn eine Aktion ausgeführt wird, und darauf warten, bevor wir die Funktion fortsetzen. Anstelle einer Folge von Zahlen brauchen wir also eine Folge zukünftiger Werte - das heißt: Versprechen.
Mit diesem etwas kniffligen, aber sehr mächtigen Trick können wir asynchronen Code synchron schreiben. Es gibt mehrere "Läufer", die dies für Sie tun. Das Schreiben einer ist ein paar kurze Codezeilen, die jedoch den Rahmen dieser Antwort sprengen. Ich werde hier Bluebirds verwenden
Promise.coroutine
, aber es gibt andere Wrapper wieco
oderQ.async
.Diese Methode gibt selbst ein Versprechen zurück, das wir von anderen Coroutinen konsumieren können. Zum Beispiel:
ES2016 (ES7)
In ES7 ist dies weiter standardisiert, es gibt derzeit mehrere Vorschläge, aber in allen können Sie
await
versprechen. Dies ist nur "Zucker" (schönere Syntax) für den obigen ES6-Vorschlag durch Hinzufügen der Schlüsselwörterasync
undawait
. Machen Sie das obige Beispiel:Es gibt trotzdem ein Versprechen zurück :)
quelle
return await data.json();
?)Sie verwenden Ajax falsch. Die Idee ist, dass nichts zurückgegeben wird, sondern die Daten an eine sogenannte Rückruffunktion übergeben werden, die die Daten verarbeitet.
Das ist:
Wenn Sie etwas im Submit-Handler zurückgeben, wird nichts ausgeführt. Sie müssen stattdessen entweder die Daten übergeben oder direkt in der Erfolgsfunktion damit tun, was Sie wollen.
quelle
success: handleData
und es würde funktionieren.success
Methode, sondern der umgebende Bereich von$.ajax
.Die einfachste Lösung besteht darin, eine JavaScript-Funktion zu erstellen und für den Ajax-
success
Rückruf aufzurufen .quelle
Ich werde mit einem schrecklich aussehenden, handgezeichneten Comic antworten. Das zweite Bild ist der Grund , warum
result
istundefined
in Ihrem Codebeispiel.quelle
Angular1
Für Menschen, die AngularJS verwenden , kann diese Situation mit umgehen
Promises
.Hier heißt es:
Sie können eine schöne Erklärung finden hier auch.
Beispiel in den unten genannten Dokumenten .
Angular2 und höher
In
Angular2
mit Blick auf das folgende Beispiel, aber seine empfohlen zu VerwendungObservables
mitAngular2
.}}
Sie können das auf diese Weise konsumieren,
Siehe den Originalbeitrag hier. Typescript unterstützt jedoch keine nativen es6-Versprechen . Wenn Sie es verwenden möchten, benötigen Sie möglicherweise ein Plugin dafür.
Außerdem ist hier die Versprechen SPEC hier definieren.
quelle
Die meisten Antworten hier geben nützliche Vorschläge, wenn Sie eine einzelne asynchrone Operation ausführen. Manchmal tritt dies jedoch auf, wenn Sie für jeden Eintrag in einem Array oder einer anderen listenartigen Struktur eine asynchrone Operation ausführen müssen . Die Versuchung ist, dies zu tun:
Beispiel:
Code-Snippet anzeigen
Der Grund, der nicht funktioniert, ist, dass die Rückrufe von
doSomethingAsync
noch nicht ausgeführt wurden, als Sie versuchen, die Ergebnisse zu verwenden.Wenn Sie also ein Array (oder eine Liste) haben und für jeden Eintrag asynchrone Operationen ausführen möchten, haben Sie zwei Möglichkeiten: Führen Sie die Operationen parallel (überlappend) oder in Reihe (nacheinander nacheinander) aus.
Parallel
Sie können alle starten und verfolgen, wie viele Rückrufe Sie erwarten, und dann die Ergebnisse verwenden, wenn Sie so viele Rückrufe erhalten haben:
Beispiel:
Code-Snippet anzeigen
(Wir könnten abschaffen
expecting
und nur verwendenresults.length === theArray.length
, aber das lässt uns offen für die Möglichkeit,theArray
die geändert wird, während die Anrufe ausstehen ...)Beachten Sie, wie wir das
index
from verwendenforEach
, um das Ergebnis anresults
derselben Position wie den Eintrag zu speichern, auf den es sich bezieht, auch wenn die Ergebnisse nicht in der richtigen Reihenfolge eintreffen (da asynchrone Aufrufe nicht unbedingt in der Reihenfolge abgeschlossen werden müssen, in der sie gestartet wurden).Aber was , wenn Sie brauchen , um zurückzukehren diese Ergebnisse aus einer Funktion? Wie die anderen Antworten gezeigt haben, können Sie nicht; Ihre Funktion muss einen Rückruf annehmen und zurückrufen (oder ein Versprechen zurückgeben ). Hier ist eine Rückrufversion:
Beispiel:
Code-Snippet anzeigen
Oder hier ist eine Version, die
Promise
stattdessen a zurückgibt:Wenn
doSomethingAsync
uns Fehler übergeben werden,reject
lehnen wir das Versprechen natürlich ab, wenn wir einen Fehler erhalten.)Beispiel:
Code-Snippet anzeigen
(Alternativ können Sie auch einen Wrapper erstellen
doSomethingAsync
, der ein Versprechen zurückgibt, und dann die folgenden Schritte ausführen ...)Wenn
doSomethingAsync
Sie ein Versprechen erhalten , können Sie Folgendes verwendenPromise.all
:Wenn Sie wissen,
doSomethingAsync
dass ein zweites und ein drittes Argument ignoriert werden, können Sie es direkt an übergebenmap
(map
ruft den Rückruf mit drei Argumenten auf, aber die meisten Leute verwenden meistens nur das erste):Beispiel:
Code-Snippet anzeigen
Beachten Sie, dass
Promise.all
das Versprechen mit einer Reihe von Ergebnissen aller Versprechen aufgelöst wird, die Sie ihm geben, wenn sie alle gelöst sind, oder das Versprechen ablehnt, wenn das erste der Versprechen, die Sie ihm geben, abgelehnt wird.Serie
Angenommen, Sie möchten nicht, dass die Operationen parallel ablaufen? Wenn Sie sie nacheinander ausführen möchten, müssen Sie warten, bis jeder Vorgang abgeschlossen ist, bevor Sie mit dem nächsten beginnen. Hier ist ein Beispiel für eine Funktion, die dies tut und einen Rückruf mit dem Ergebnis aufruft:
(Da wir die Arbeit in Serie
results.push(result)
ausführen , können wir sie nur verwenden, da wir wissen, dass wir keine fehlerhaften Ergebnisse erzielen. Oben hätten wir sie verwenden könnenresults[index] = result;
, aber in einigen der folgenden Beispiele haben wir keinen Index benutzen.)Beispiel:
Code-Snippet anzeigen
(Oder bauen Sie erneut einen Wrapper
doSomethingAsync
, der Ihnen ein Versprechen gibt, und führen Sie die folgenden Schritte aus ...)Wenn
doSomethingAsync
Sie ein Versprechen erhalten, wenn Sie die ES2017 + -Syntax verwenden können (möglicherweise mit einem Transpiler wie Babel ), können Sie eineasync
Funktion mitfor-of
und verwendenawait
:Beispiel:
Code-Snippet anzeigen
Wenn Sie die ES2017 + -Syntax (noch) nicht verwenden können, können Sie eine Variation des Musters "Versprechen reduzieren" verwenden (dies ist komplexer als das übliche Versprechen reduzieren, da wir das Ergebnis nicht von einem zum nächsten weitergeben, sondern stattdessen Sammeln ihrer Ergebnisse in einem Array):
Beispiel:
Code-Snippet anzeigen
... was mit ES2015 + Pfeilfunktionen weniger umständlich ist :
Beispiel:
Code-Snippet anzeigen
quelle
if (--expecting === 0)
bitte erklären, wie der Teil des Codes funktioniert? Die Callback-Version Ihrer Lösung funktioniert hervorragend für mich. Ich verstehe nur nicht, wie Sie mit dieser Aussage die Anzahl der abgeschlossenen Antworten überprüfen. Schätzen Sie, dass es nur ein Mangel an Wissen meinerseits ist. Gibt es eine alternative Möglichkeit, einen Scheck zu schreiben?expecting
Beginnt mit dem Wert von. Dies ist die Anzahl derarray.length
Anfragen, die wir stellen werden. Wir wissen, dass der Rückruf erst aufgerufen wird, wenn alle diese Anforderungen gestartet wurden. Im Rückruf wirdif (--expecting === 0)
Folgendes ausgeführt: 1. Dekrementeexpecting
(wir haben eine Antwort erhalten, daher erwarten wir eine Antwort weniger) und wenn der Wert nach dem Dekrement 0 ist (wir erwarten keine weiteren Antworten), sind wir erledigt!results
nicht vorhanden war). :-) Repariert.Schauen Sie sich dieses Beispiel an:
Wie Sie sehen können,
getJoke
wird ein gelöstes Versprechen zurückgegeben (es wird bei der Rückkehr gelöstres.data.value
). Sie warten also, bis die Anforderung $ http.get abgeschlossen ist, und führen dann console.log (res.joke) aus (als normaler asynchroner Ablauf).Dies ist die plnkr:
http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/
ES6 Weg (asynchron - warten)
quelle
Dies ist einer der Orte, an denen Datenbindungs- oder Speicherkonzepte auf zwei Arten , die in vielen neuen JavaScript-Frameworks verwendet werden, für Sie hervorragend geeignet sind ...
Wenn Sie also Angular, React oder ein anderes Framework verwenden, das Datenbindungs- oder Speicherkonzepte auf zwei Arten ausführt , wird dieses Problem einfach für Sie behoben. Kurz gesagt, Ihr Ergebnis befindet sich
undefined
in der ersten Phase, sodass Sie es erhalten,result = undefined
bevor Sie das erhalten Daten, sobald Sie das Ergebnis erhalten, werden sie aktualisiert und dem neuen Wert zugewiesen, der auf Ihren Ajax-Aufruf reagiert ...Aber wie können Sie es zum Beispiel in reinem Javascript oder jQuery machen, wie Sie in dieser Frage gefragt haben?
Sie können einen Rückruf , ein Versprechen und eine kürzlich beobachtbare Funktion verwenden , um dies für Sie zu erledigen. Beispielsweise haben wir in Versprechungen eine Funktion wie
success()
oder,then()
die ausgeführt wird, wenn Ihre Daten für Sie bereit sind, ebenso wie die Rückruf- oder Abonnementfunktion für beobachtbar .In Ihrem Fall, in dem Sie jQuery verwenden , können Sie beispielsweise Folgendes tun:
Weitere Informationen finden Sie in den Versprechungen und Observablen, die neuere Methoden für diese asynchronen Aufgaben darstellen.
quelle
$.ajax({url: "api/data", success: fooDone.bind(this)});
Es ist ein sehr häufiges Problem, mit dem wir konfrontiert sind, wenn wir mit den "Geheimnissen" von JavaScript kämpfen. Lassen Sie mich heute versuchen, dieses Rätsel zu entmystifizieren.
Beginnen wir mit einer einfachen JavaScript-Funktion:
Dies ist ein einfacher synchroner Funktionsaufruf (bei dem jede Codezeile vor der nächsten nacheinander mit ihrem Job "fertig" ist) und das Ergebnis das gleiche wie erwartet ist.
Fügen wir nun ein wenig Twist hinzu, indem wir unsere Funktion geringfügig verzögern, damit nicht alle Codezeilen nacheinander "fertig" sind. Somit wird das asynchrone Funktionsverhalten emuliert:
Also los, diese Verzögerung hat gerade die Funktionalität gebrochen, die wir erwartet hatten! Aber was genau ist passiert? Nun, es ist eigentlich ziemlich logisch, wenn man sich den Code ansieht. Die Funktion
foo()
gibt bei der Ausführung nichts zurück (der zurückgegebene Wert ist alsoundefined
), startet jedoch einen Timer, der eine Funktion nach 1s ausführt, um 'wohoo' zurückzugeben. Aber wie Sie sehen können, ist der Wert, der bar zugewiesen wird, das sofort zurückgegebene Material von foo (), was nichts anderes ist als gerechtundefined
.Wie gehen wir dieses Problem an?
Fragen wir unsere Funktion nach einem VERSPRECHEN . Bei Promise geht es wirklich darum, was es bedeutet: Es bedeutet, dass die Funktion Ihnen garantiert, dass Sie jede Ausgabe liefern, die es in Zukunft erhält. Lassen Sie es uns für unser kleines Problem oben in Aktion sehen:
Die Zusammenfassung lautet also: Um die asynchronen Funktionen wie Ajax-basierte Aufrufe usw. anzugehen, können Sie ein Versprechen für
resolve
den Wert verwenden (den Sie zurückgeben möchten). Kurz gesagt, Sie lösen den Wert in asynchronen Funktionen auf , anstatt ihn zurückzugeben .UPDATE (Versprechen mit Async / Warten)
Neben der Verwendung
then/catch
zur Arbeit mit Versprechungen gibt es noch einen weiteren Ansatz. Die Idee ist, eine asynchrone Funktion zu erkennen und dann auf die Auflösung der Versprechen zu warten , bevor mit der nächsten Codezeile fortgefahren wird. Es ist immer noch nurpromises
unter der Haube, aber mit einem anderen syntaktischen Ansatz. Um die Dinge klarer zu machen, finden Sie unten einen Vergleich:dann / catch Version:
asynchrone / warte Version:
quelle
Ein anderer Ansatz, um einen Wert von einer asynchronen Funktion zurückzugeben, besteht darin, ein Objekt zu übergeben, das das Ergebnis der asynchronen Funktion speichert.
Hier ist ein Beispiel dafür:
Ich verwende das
result
Objekt, um den Wert während der asynchronen Operation zu speichern. Dadurch ist das Ergebnis auch nach dem asynchronen Job verfügbar.Ich benutze diesen Ansatz oft. Mich würde interessieren, wie gut dieser Ansatz funktioniert, wenn das Ergebnis über aufeinanderfolgende Module zurückverdrahtet wird.
quelle
result
. Dies funktioniert, weil Sie die Variable nach Abschluss der asynchronen Funktion lesen .Während Versprechen und Rückrufe in vielen Situationen gut funktionieren, ist es ein Problem, etwas auszudrücken wie:
Sie würden am Ende durchgehen
async1
; Überprüfen Sie, obname
undefiniert ist oder nicht, und rufen Sie den Rückruf entsprechend auf.Während es in kleinen Beispielen in Ordnung ist, wird es ärgerlich, wenn Sie viele ähnliche Fälle und Fehlerbehandlungen haben.
Fibers
hilft bei der Lösung des Problems.Sie können das Projekt hier auschecken .
quelle
async-await
wenn Sie einige der neuesten Versionen von Node verwenden. Wenn jemand mit älteren Versionen nicht weiterkommt, kann er diese Methode verwenden.Das folgende Beispiel, das ich geschrieben habe, zeigt, wie es geht
Dieses Arbeitsbeispiel ist in sich geschlossen. Es wird ein einfaches Anforderungsobjekt definiert, das das Fensterobjekt
XMLHttpRequest
zum Tätigen von Anrufen verwendet. Es wird eine einfache Funktion definiert, mit der darauf gewartet werden kann, dass eine Reihe von Versprechungen erfüllt werden.Kontext. In diesem Beispiel wird der Spotify-Web-API- Endpunkt
playlist
abgefragt , um nach Objekten für einen bestimmten Satz von Abfragezeichenfolgen zu suchen :Für jedes Element löst ein neues Versprechen einen Block aus
ExecutionBlock
. Analysieren Sie das Ergebnis, planen Sie einen neuen Satz von Versprechen basierend auf dem Ergebnisarray, dh einer Liste von Spotify-user
Objekten, und führen Sie den neuen HTTP-Aufruf innerhalb desExecutionProfileBlock
asynchronen Versuchs aus.Sie können dann eine verschachtelte Promise-Struktur sehen, mit der Sie mehrere und vollständig asynchrone verschachtelte HTTP-Aufrufe erzeugen und die Ergebnisse aus jeder Teilmenge von Aufrufen zusammenführen können
Promise.all
.HINWEIS Für neuere Spotify-
search
APIs muss ein Zugriffstoken in den Anforderungsheadern angegeben werden:Um das folgende Beispiel auszuführen, müssen Sie Ihr Zugriffstoken in die Anforderungsheader einfügen:
Ich habe diese Lösung hier ausführlich besprochen .
quelle
Die kurze Antwort lautet: Sie müssen einen Rückruf wie folgt implementieren:
quelle
Antwort 2017: Sie können jetzt in jedem aktuellen Browser und Knoten genau das tun, was Sie wollen
Das ist ganz einfach:
Hier ist eine funktionierende Version Ihres Codes:
Warten wird in allen aktuellen Browsern und Knoten 8 unterstützt
quelle
await/async
Der Browser kann in drei Teile unterteilt werden:
1) Ereignisschleife
2) Web-API
3) Ereigniswarteschlange
Die Ereignisschleife wird für immer ausgeführt, dh für eine Art Endlosschleife. In der Ereigniswarteschlange werden alle Ihre Funktionen auf ein Ereignis verschoben (Beispiel: Klicken). Dies wird einzeln aus der Warteschlange ausgeführt und in die Ereignisschleife gestellt, die diese Funktion ausführt und selbst vorbereitet Dies bedeutet, dass die Ausführung einer Funktion erst beginnt, wenn die Funktion, bevor sie sich in der Warteschlange befindet, in der Ereignisschleife ausgeführt wird.
Nehmen wir nun an, wir haben zwei Funktionen in eine Warteschlange verschoben. Eine dient zum Abrufen von Daten vom Server und eine andere verwendet diese Daten. Wir haben zuerst die Funktion serverRequest () in die Warteschlange gestellt und dann die Funktion useData () verwendet. Die Funktion serverRequest geht in die Ereignisschleife und ruft den Server auf, da wir nie wissen, wie viel Zeit erforderlich ist, um Daten vom Server abzurufen. Daher wird erwartet, dass dieser Prozess einige Zeit in Anspruch nimmt, und wir beschäftigen unsere Ereignisschleife, sodass unsere Seite hängt. Dort hängt das Web Die API kommt in die Rolle, indem sie diese Funktion aus der Ereignisschleife übernimmt und sich mit dem Server befasst, der die Ereignisschleife frei macht, damit wir die nächste Funktion aus der Warteschlange ausführen können. Die nächste Funktion in der Warteschlange ist utiliseData (), die in die Schleife geht, aber da keine Daten verfügbar sind Verschwendung und Ausführung der nächsten Funktion werden bis zum Ende der Warteschlange fortgesetzt. (Dies wird als Async-Aufruf bezeichnet, dh wir können etwas anderes tun, bis wir Daten erhalten.)
Angenommen, unsere Funktion serverRequest () hatte eine return-Anweisung in einem Code. Wenn wir Daten von der Server-Web-API zurückerhalten, werden sie am Ende der Warteschlange in die Warteschlange gestellt. Da es am Ende in die Warteschlange verschoben wird, können wir seine Daten nicht verwenden, da in unserer Warteschlange keine Funktion mehr zum Verwenden dieser Daten vorhanden ist. Daher ist es nicht möglich, etwas von Async Call zurückzugeben.
Die Lösung hierfür ist also ein Rückruf oder Versprechen .
Ein Bild aus einer der Antworten hier, erklärt die Verwendung von Rückrufen korrekt ... Wir geben unsere Funktion (Funktion unter Verwendung der vom Server zurückgegebenen Daten) an den aufrufenden Server weiter.
In meinem Code heißt es als
Rückruf von Javscript.info
quelle
Sie können diese benutzerdefinierte Bibliothek (geschrieben mit Promise) verwenden, um einen Remote-Anruf zu tätigen.
Einfaches Verwendungsbeispiel:
quelle
Eine andere Lösung besteht darin, Code über den sequentiellen Executor nsynjs auszuführen .
Wenn die zugrunde liegende Funktion versprochen ist
nsynjs wertet alle Versprechen nacheinander aus und legt das Versprechen-Ergebnis in die
data
Eigenschaft:Wenn die zugrunde liegende Funktion nicht versprochen wird
Schritt 1. Wrap-Funktion mit Rückruf in nsynjs-fähigen Wrapper (wenn die Version versprochen ist, können Sie diesen Schritt überspringen):
Schritt 2. Synchrone Logik in Funktion setzen:
Schritt 3. Führen Sie die Funktion synchron über nsynjs aus:
Nsynjs wertet alle Operatoren und Ausdrücke Schritt für Schritt aus und unterbricht die Ausführung, falls das Ergebnis einer langsamen Funktion nicht bereit ist.
Weitere Beispiele hier: https://github.com/amaksr/nsynjs/tree/master/examples
quelle
ECMAScript 6 verfügt über 'Generatoren', mit denen Sie problemlos asynchron programmieren können.
Um den obigen Code auszuführen, gehen Sie folgendermaßen vor:
Wenn Sie auf Browser abzielen müssen, die ES6 nicht unterstützen, können Sie den Code über Babel oder den Closure-Compiler ausführen, um ECMAScript 5 zu generieren.
Die Rückrufe
...args
werden in ein Array eingeschlossen und beim Lesen zerstört, sodass das Muster Rückrufe mit mehreren Argumenten verarbeiten kann. Zum Beispiel mit dem Knoten fs :quelle
Hier sind einige Ansätze zum Arbeiten mit asynchronen Anforderungen:
Beispiel: Die Implementierung von jQuery wurde verschoben, um mit mehreren Anforderungen zu arbeiten
quelle
Wir befinden uns in einem Universum, das sich in einer Dimension zu entwickeln scheint, die wir "Zeit" nennen. Wir verstehen nicht wirklich, was Zeit ist, aber wir haben Abstraktionen und Vokabeln entwickelt, mit denen wir argumentieren und darüber sprechen können: "Vergangenheit", "Gegenwart", "Zukunft", "vor", "nach".
Die Computersysteme, die wir bauen, haben immer mehr Zeit als wichtige Dimension. Bestimmte Dinge sind für die Zukunft vorgesehen. Dann müssen andere Dinge passieren, nachdem diese ersten Dinge schließlich geschehen sind. Dies ist der Grundbegriff, der als "Asynchronität" bezeichnet wird. In unserer zunehmend vernetzten Welt wartet der häufigste Fall von Asynchronität darauf, dass ein Remote-System auf eine Anfrage reagiert.
Betrachten Sie ein Beispiel. Sie rufen den Milchmann an und bestellen etwas Milch. Wenn es darum geht, möchten Sie es in Ihren Kaffee geben. Sie können die Milch momentan nicht in Ihren Kaffee geben, da sie noch nicht hier ist. Sie müssen warten, bis es kommt, bevor Sie es in Ihren Kaffee geben. Mit anderen Worten, Folgendes funktioniert nicht:
Da JS hat keine Möglichkeit , zu wissen , dass es muss warten für
order_milk
zu beenden , bevor sie ausgeführt wirdput_in_coffee
. Mit anderen Worten, weiß er nicht , dassorder_milk
ist asynchron --is etwas , das erst später einmal in Milch würde zur Folge hat . JS und andere deklarative Sprachen führen eine Anweisung nach der anderen aus, ohne zu warten.Der klassische JS-Ansatz für dieses Problem, bei dem die Tatsache ausgenutzt wird, dass JS Funktionen als erstklassige Objekte unterstützt, die weitergegeben werden können, besteht darin, eine Funktion als Parameter an die asynchrone Anforderung zu übergeben, die sie nach Abschluss aufruft seine Aufgabe irgendwann in der Zukunft. Das ist der "Rückruf" -Ansatz. Es sieht aus wie das:
order_milk
startet, bestellt die Milch und ruft dann, wenn und nur wenn sie eintrifft, anput_in_coffee
.Das Problem bei diesem Rückrufansatz besteht darin, dass er die normale Semantik einer Funktion verschmutzt, mit der das Ergebnis gemeldet wird
return
. Stattdessen dürfen Funktionen ihre Ergebnisse nicht durch Aufrufen eines als Parameter angegebenen Rückrufs melden. Dieser Ansatz kann auch bei längeren Ereignissequenzen schnell unhandlich werden. Nehmen wir zum Beispiel an, ich möchte warten, bis die Milch in den Kaffee gegeben wird, und dann und erst dann einen dritten Schritt ausführen, nämlich den Kaffee zu trinken. Am Ende muss ich so etwas schreiben:wo ich
put_in_coffee
sowohl die Milch zum Einfüllen als auch die Aktion übergebe (drink_coffee
), die ausgeführt werden soll, sobald die Milch eingegeben wurde. Ein solcher Code ist schwer zu schreiben, zu lesen und zu debuggen.In diesem Fall könnten wir den Code in der Frage wie folgt umschreiben:
Versprechen eingeben
Dies war die Motivation für die Vorstellung eines "Versprechens", einer bestimmten Art von Wert, der eine Zukunft darstellt oder asynchrones Ergebnis darstellt. Es kann etwas darstellen, das bereits passiert ist oder das in Zukunft passieren wird oder möglicherweise überhaupt nicht passieren wird. Versprechen haben eine einzige Methode mit dem Namen
then
, an die Sie eine Aktion übergeben, die ausgeführt werden soll, wenn das Ergebnis, das das Versprechen darstellt, realisiert wurde.Bei unserer Milch und unserem Kaffee möchten wir
order_milk
ein Versprechen für die ankommende Milch zurückgeben und dannput_in_coffee
als angebenthen
Aktion wie folgt :Ein Vorteil davon ist, dass wir diese aneinanderreihen können, um Sequenzen zukünftiger Ereignisse zu erstellen ("Verkettung"):
Lassen Sie uns Versprechen auf Ihr spezielles Problem anwenden. Wir werden unsere Anforderungslogik in eine Funktion einschließen, die ein Versprechen zurückgibt:
Eigentlich haben wir
return
dem Anruf an nur ein hinzugefügt$.ajax
. Dies funktioniert, weil jQuery$.ajax
bereits eine Art Versprechen zurückgibt. (In der Praxis würden wir diesen Aufruf, ohne auf Details einzugehen, vorziehen, um ein echtes Versprechen zurückzugeben, oder eine Alternative dazu$.ajax
verwenden.) Nun, wenn wir die Datei laden und warten möchten, bis sie fertig ist und dann mach etwas, können wir einfach sagenzum Beispiel,
Wenn wir Versprechen verwenden, übergeben wir am Ende viele Funktionen
then
ist es oft hilfreich, die kompakteren Pfeilfunktionen im ES6-Stil zu verwenden:Das
async
SchlüsselwortAber es ist immer noch etwas vage Unbefriedigendes, Code auf eine Weise schreiben zu müssen, wenn er synchron ist, und auf eine ganz andere Art, wenn er asynchron ist. Für synchron schreiben wir
aber wenn
a
es asynchron ist, müssen wir mit Versprechungen schreibenOben haben wir gesagt: "JS kann nicht wissen, dass es warten muss, bis der erste Aufruf beendet ist, bevor der zweite ausgeführt wird." Wäre es nicht schön, wenn es ist eine Möglichkeit , JS zu sagen , dass? Es stellt sich heraus, dass es - das
await
Schlüsselwort gibt, das in einem speziellen Funktionstyp verwendet wird, der als "asynchrone" Funktion bezeichnet wird. Diese Funktion ist Teil der kommenden Version von ES, ist jedoch bereits in Transpilern wie Babel mit den richtigen Voreinstellungen verfügbar. Dies ermöglicht es uns, einfach zu schreibenIn Ihrem Fall könnten Sie so etwas schreiben
quelle
Kurze Antwort : Ihre
foo()
Methode wird sofort zurückgegeben, während der$ajax()
Aufruf nach der Rückkehr der Funktion asynchron ausgeführt wird . Das Problem ist dann, wie oder wo die vom asynchronen Aufruf nach seiner Rückkehr abgerufenen Ergebnisse gespeichert werden sollen.In diesem Thread wurden mehrere Lösungen angegeben. Am einfachsten ist es vielleicht, ein Objekt an die
foo()
Methode zu übergeben und die Ergebnisse nach Abschluss des asynchronen Aufrufs in einem Mitglied dieses Objekts zu speichern.Beachten Sie, dass der Aufruf von
foo()
immer noch nichts Nützliches zurückgibt. Das Ergebnis des asynchronen Aufrufs wird nun jedoch in gespeichertresult.response
.quelle
Verwenden Sie eine
callback()
Funktion innerhalb desfoo()
Erfolgs. Versuchen Sie es auf diese Weise. Es ist einfach und leicht zu verstehen.quelle
Die Frage war:
was interpretiert werden kann als:
Die Lösung besteht darin, Rückrufe zu vermeiden und eine Kombination aus Versprechen und asynchronem / Warten zu verwenden .
Ich möchte ein Beispiel für eine Ajax-Anfrage geben.
(Obwohl es in Javascript geschrieben werden kann, ziehe ich es vor, es in Python zu schreiben und es mit Transcrypt in Javascript zu kompilieren . Es wird klar genug sein.)
Aktivieren Sie zunächst die Verwendung von JQuery, um Folgendes
$
verfügbar zu machenS
:Definieren Sie eine Funktion, die ein Versprechen zurückgibt , in diesem Fall einen Ajax-Aufruf:
Verwenden Sie den asynchronen Code so, als wäre er synchron :
quelle
Versprechen verwenden
Die perfekteste Antwort auf diese Frage ist die Verwendung von
Promise
.Verwendungszweck
Aber warte...!
Es gibt ein Problem mit der Verwendung von Versprechungen!
Warum sollten wir unser eigenes Versprechen verwenden?
Ich habe diese Lösung eine Weile verwendet, bis ich herausgefunden habe, dass in alten Browsern ein Fehler vorliegt:
Also habe ich beschlossen, meine eigene Promise-Klasse für ES3 unter js Compilern zu implementieren, wenn sie nicht definiert ist. Fügen Sie diesen Code einfach vor Ihrem Hauptcode hinzu und verwenden Sie dann Promise!
quelle
Natürlich gibt es viele Ansätze wie synchrone Anfragen, Versprechen, aber meiner Erfahrung nach sollten Sie den Rückrufansatz verwenden. Das asynchrone Verhalten von Javascript ist natürlich. Ihr Code-Snippet kann also etwas anders geschrieben werden:
quelle
Anstatt Code auf Sie zu werfen, gibt es zwei Konzepte, die entscheidend dafür sind, wie JS mit Rückrufen und Asynchronität umgeht. (Ist das überhaupt ein Wort?)
Das Ereignisschleifen- und Parallelitätsmodell
Es gibt drei Dinge, die Sie beachten müssen; Die Warteschlange; die Ereignisschleife und der Stapel
Im weitesten Sinne ist die Ereignisschleife wie der Projektmanager. Sie wartet ständig auf Funktionen, die ausgeführt werden sollen, und kommuniziert zwischen der Warteschlange und dem Stapel.
Sobald es eine Nachricht zum Ausführen von etwas erhält, fügt es sie der Warteschlange hinzu. Die Warteschlange ist die Liste der Dinge, die auf ihre Ausführung warten (wie Ihre AJAX-Anfrage). stell es dir so vor:
Wenn eine dieser Nachrichten ausgeführt wird, wird die Nachricht aus der Warteschlange entfernt und ein Stapel erstellt. Der Stapel ist alles, was JS ausführen muss, um die Anweisung in der Nachricht auszuführen. In unserem Beispiel wird also gesagt, dass wir anrufen sollen
foobarFunc
Alles, was foobarFunc ausführen muss (in unserem Fall
anotherFunction
), wird auf den Stapel verschoben . ausgeführt und dann vergessen - die Ereignisschleife wechselt dann zum nächsten Element in der Warteschlange (oder wartet auf Nachrichten).Der Schlüssel hier ist die Reihenfolge der Ausführung. Das ist
WANN wird etwas laufen
Wenn Sie mit AJAX einen Anruf bei einer externen Partei tätigen oder einen asynchronen Code ausführen (z. B. ein setTimeout), ist Javascript von einer Antwort abhängig, bevor es fortgesetzt werden kann.
Die große Frage ist, wann die Antwort kommt. Die Antwort ist, dass wir es nicht wissen - also wartet die Ereignisschleife darauf, dass diese Nachricht "hey run me" lautet. Wenn JS nur synchron auf diese Nachricht warten würde, würde Ihre App einfrieren und sie würde saugen. Daher führt JS das nächste Element in der Warteschlange weiter aus, während er darauf wartet, dass die Nachricht wieder zur Warteschlange hinzugefügt wird.
Aus diesem Grund verwenden wir bei asynchronen Funktionen sogenannte Rückrufe . Es ist buchstäblich wie ein Versprechen . Wie in Ich verspreche, irgendwann etwas zurückzugeben, verwendet jQuery bestimmte Rückrufe namens
deffered.done
deffered.fail
unddeffered.always
(unter anderem). Sie können sie alle hier sehenSie müssen also eine Funktion übergeben, deren Ausführung versprochen wird, und zwar mit Daten, die an sie übergeben werden.
Da ein Rückruf nicht sofort ausgeführt wird, sondern zu einem späteren Zeitpunkt, ist es wichtig, den Verweis auf die nicht ausgeführte Funktion zu übergeben. damit
Die meiste Zeit (aber nicht immer) wirst du
foo
nicht bestehenfoo()
Hoffentlich macht das Sinn. Wenn Sie auf solche Dinge stoßen, die verwirrend erscheinen, empfehle ich dringend, die Dokumentation vollständig zu lesen, um zumindest ein Verständnis dafür zu bekommen. Es wird Sie zu einem viel besseren Entwickler machen.
quelle
Mit ES2017 sollten Sie dies als Funktionsdeklaration haben
Und es so ausführen.
Oder die Promise-Syntax
quelle