Tiefgreifendes Verständnis des asynchronen Wartens in Javascript

Tiefgreifendes Verständnis des asynchronen Wartens in Javascript

In diesem Artikel untersuchen wir, warum async/await das bevorzugte Tool jedes Javascript-Entwicklers für die asynchrone Programmierung ist. Wenn Sie mit JavaScript noch nicht vertraut sind, machen Sie sich keine Sorgen. Dieser Artikel hilft Ihnen dabei, Async/Await von Grund auf zu verstehen.

einführen

Async/await ist ein Muster in JavaScript, das die synchrone Ausführung Ihres Codes ermöglicht, ohne das asynchrone Verhalten von JavaScript zu beeinträchtigen.

Definieren asynchroner Funktionen

Um eine asynchrone Funktion zu definieren, müssen Sie lediglich vor der Funktionsdefinition ein Schlüsselwort „async“ hinzufügen.

// Die asynchrone Funktion gibt immer ein Versprechen zurück
asynchrone Funktion greet() {
  gib "hallo" zurück;
}

Entspannen Sie sich und machen Sie es sich bequem! 😎. Verwenden Sie das Schlüsselwort „async“ vor dem Funktionsnamen.

Sorgen Sie dafür, dass die Funktion ein Versprechen zurückgibt.

Wird analysiert, wenn die Funktion zurückkehrt.

Endgültige Ablehnung, wenn ein Fehler auftritt.

Dies bedeutet, dass Sie nicht jedes Mal „return Promise.new()“ deklarieren müssen, wenn Sie ein Promise erstellen möchten.

Um zu zeigen, dass die asynchrone Funktion ein Promise zurückgibt, können wir schnell einen Then-Block anhängen, um dessen Wert auszudrucken.

asynchrone Funktion greet() {
  returniere „Hallo von einer asynchronen Funktion“
}
greet().then(Nachricht => console.log(Nachricht));
//Hallo von einer asynchronen Funktion

Verwenden von „await“ und Ausführen asynchroner Funktionen

Ist es nicht cool, dass wir then() und catch() in einer asynchronen Funktion ausführen können? Dies ist jedoch nicht die wirkliche Funktionalität asynchroner Funktionen. Das wahre Potenzial der Funktionen liegt in der „await“-Anweisung.

„await“ bewirkt, dass eine Funktion synchron ausgeführt wird, während die Kontrolle in dieser Zeile gehalten wird, bis die erwartete Methode ihre Ausführung abgeschlossen hat.

asynchrone Funktion greet() {
  returniere „Hallo von einer asynchronen Funktion“
}

asynchrone Funktion ausführen() {
  const message = warte auf Begrüßung();
  console.log(Nachricht)
}

Hier sind einige Faustregeln, die wir uns merken müssen.

👉Wait kann nur in asynchronen Funktionen verwendet werden

Wenn wir await innerhalb einer Funktion verwenden, müssen wir eine Funktion deklarieren, aber nicht umgekehrt.

Lassen Sie es mich so sagen. Wenn Sie „Await“-Anweisungen innerhalb einer Methode verwenden, muss es sich bei dieser Methode um eine asynchrone Methode handeln, sonst gibt der Compiler ein Problem aus.

asynchrone Funktion greet() {
  returniere „Hallo von einer asynchronen Funktion“;
}

function execute() { //diese Funktion muss asynchron sein
  const message = warte auf Begrüßung();
  console.log(Nachricht)
}
/* 
SyntaxError: „await“ ist nur in der asynchronen Funktion gültig.
*/

Aber das Deklarieren einer Funktion als asynchron bedeutet nicht unbedingt, dass wir darin immer darauf warten. Hier ist greet() eine asynchrone Methode, sie enthält aber keine Await-Anweisung.

Warten ist nur beim Aufrufen einer Funktion sinnvoll, die ein Versprechen zurückgibt oder eine asynchrone Funktion ist.

//keine asynchrone Funktion
Funktion Begrüßung() {
 returniere „Hallo von einer asynchronen Funktion“;
}

asynchrone Funktion ausführen() {
  const message = warte auf Begrüßung();
  console.log(message); //Hallo von einer asynchronen Funktion
}

Obwohl der Code genauso funktioniert wie der vorherige, macht es keinen Sinn, dass await auf eine synchrone Funktion zugreift. Ich würde gerne wissen, was Sie darüber denken?

Ein wichtiger Aspekt bei der Verwendung von „await“ besteht darin, dass die Ausführung der nächsten Codezeile blockiert wird, bis der „await“-Block ausgeführt wird.

const asyncGreet = () => neues Versprechen(auflösen => setTimeout(auflösen, 2000));

(asynchrone Funktion ausführen() {
  console.log("vor der Ausführung");
  await asyncGreet(); //blockiert die Ausführung hier
  // 👇 wird ausgeführt, sobald der Wartevorgang abgeschlossen ist
  console.log("Ich werde nach 2000ms ausgeführt");
})();

Jetzt fragen Sie sich bestimmt: Wenn „wait“ den Code synchronisiert, warum verwenden wir es dann? NodeJs oder Browser-Javascript sind Single-Thread-Umgebungen, die jeweils eine Aufgabe ausführen und aufgrund ihres asynchronen Verhaltens, das wir verlieren, weit verbreitet sind. Was ist dann der Sinn?

Ja, Sie haben Recht, aber beobachten Sie, dass wir in den meisten Fällen Aufgaben in Beziehung zu anderen ausführen müssen.

asynchrone Funktion subscribeToNewsLetter() {
  const Benutzer = warte auf Benutzer finden(ID);
  //👇Methoden benötigen zur Ausführung die E-Mail-Adresse des Benutzers
  warte auf Abonnement (Benutzer-E-Mail)
  warte auf sendNotification(user.email)
}

Stimmt, aber was ist mit nicht damit zusammenhängendem Code? Nun, es gibt eine alternative Methode, nämlich (Promise.all).

const asyncGreet = (Name) => neues Promise((Auflösung) => setTimeout(Auflösung(`Hallo ${name}`), 2000));

Konstantennamen = ['John', 'Jane', 'David'];

(asynchrone Funktion() {
  const GreetingPromises = Namen.map(name => asyncGreet(name));
  console.log (warte auf Promise.all (GreetingPromises));
})();

Ich weiß, dass der obige Code ein konstruiertes Beispiel ist. Wichtig ist hier, dass wir die Leistung von Promise.all nutzen, um alle Versprechen auszuführen.

Fehlerbehandlung mit Async/Await.

Die Fehlerbehandlung mit async/await ist ziemlich einfach und wir können hierzu unseren alten Freund, den Try/Catch-Block, verwenden.

asynchrone Funktion subscribeToNewsLetter() {
  versuchen {
    const Benutzer = warte auf Benutzer finden(ID);
    warte auf Abonnement (Benutzer-E-Mail)
    warte auf sendNotification(user.email)
  } Fang(Fehler) {
    //Fehler behandeln
  }
}

Es gibt eine andere Version, bei der wir den Catch-Handler direkt an den Await-Block anhängen können. Ich persönlich verwende es nicht, aber Sie können es ausprobieren, wenn Sie möchten.

  warte auf asyncGreet().catch(err => console.log(err);

2x besser lesbar und einfacher zu debuggen

Der folgende Code verwendet ein Promise, um einen Benutzer anhand der ID zu finden, die Profilinformationen zuzuweisen und dann die Abonnements des Benutzers zu finden.

Funktion getUser(id, Profil) {
  returniere neues Promise((lösen, ablehnen) => {
    Benutzer
      .find(ich würde)
      .then((Benutzer) => {
        if(_.isEmpty(Benutzer)) return {};
        Benutzer.Profil = Profil;
        Benutzer zurückgeben;
      })
      .then((Benutzer) => Abonnement.find(Benutzer-ID))
      .dann(Abonnement => {
        wenn(_.isEmpty(Abonnement)) {
          Benutzer.Abonnement = null;
        } anders {
          Benutzer.Abonnement = Abonnement;
        }
        returniere Auflösung (Benutzer)
      })
      .catch(err => ablehnen(err))
  })
}

Der obige Code funktioniert einwandfrei, aber wir können ihn mit async/await definitiv lesbarer, prägnanter und einfacher zu debuggen machen. Lass uns gehen.

asynchrone Funktion getUser(id, profile) {
  versuchen {
    const Benutzer = warte auf Benutzer.find(id);
    if(_.isEmpty(Benutzer)) return {};
    Benutzer.Profil = Profil;
    const-Abonnement = warte auf Abonnement.find(Benutzer-ID);
    user.subscription = Abonnement
    Benutzer zurückgeben;
  } Fang(Fehler) {
    console.log(fehler);
  }
}

Callbacks und Async/Await sind Feinde

Wie wir bereits in vorherigen Beispielen gesehen haben, funktionieren Promises sehr gut mit async/await. Jede Funktion, die ein Versprechen zurückgibt, kann mit der Await-Anweisung verwendet werden.

Bei Callbacks ist jedoch das Gegenteil der Fall. Callbacks können nicht direkt mit async/await verwendet werden. Sie müssen in Promises umgewandelt werden.

Betrachten wir die folgende Funktion, die asynchron prüft, ob ein Wert gerade ist (gibt einen Fehler aus).

Funktion asyncEven(id, cb){
  setzeTimeout(() => {
    const gerade = id%2 === 0;
    wenn (gerade) return cb(null, "gerade");
    sonst returniere cb("nicht einmal");
  }, 2000);
}

Wir wissen, dass „await“ in Rückrufen nicht zulässig ist, aber versuchen wir es trotzdem.

(asynchrone Funktion() {
  //🐶👹 Falscher Weg
  const even = warte auf asyncEven(2);
  console.log("isEven ", gerade); //undefiniert
})();

Sie denken bestimmt, wir hätten keinen Rückruf angehängt und deshalb wird „undefined“ ausgegeben.

Lassen Sie uns einen Rückruf anhängen, was seltsam ist, aber lassen Sie uns geduldig sein.

(asynchrone Funktion() {
  //das ist auch falsch 🐶👹
  const even = await asyncEven(2, (err, data) => { console.log("innerhalb von await auf Rückruf", err, data)});
  console.log("istGerade ", gerade);
})();
/*
Ausgabe:
sogar undefiniert
im Inneren warten auf Rückruf sogar null
*/ 

Es scheint, dass der Rückruf aufgerufen wird und wir auch den Wert von der Funktion asyncEven erhalten. Das stimmt, aber es ist trotzdem der falsche Ansatz.

„await“ hat keine Auswirkungen auf Rückrufe. Dies ist vergleichbar mit dem Warten auf eine synchrone Funktion.

Warum wird also „undefiniert“ zurückgegeben? Das ist eine gute Frage. Dies ist die Standardnatur der asynchronen Programmierung. Die Funktion setTimeout ist ein Rückruf, der den Rückrufwert nach 2000 Millisekunden zurückgibt, während das Steuerelement mit der Ausführung der nächsten Codezeile beginnt und die Funktion erreicht. Deshalb erhalten wir am Ende „undefiniert“.

Was ist also die Lösung? Es ist ganz einfach, die Funktion „asyncEven“ in ein Versprechen umzuwandeln und „await“ wie ein Champion zu verwenden.

Funktion asyncEven(id,) {
  returniere neues Promise((lösen, ablehnen) => {
    setzeTimeout(() => {
      const gerade = id%2 === 0;
      wenn (gerade) return resolve("gerade");
      sonst returniere „ablehnen(‚nicht einmal‘);
    }, 2000);
  })
}

(asynchrone Funktion() {
  // wartet auf die Ausführung
  const even = warte auf asyncEven(2);
  console.log("ist gerade ", gerade);
})();

ForEach ist nicht für Async/Await geeignet

Es kann zu Nebenwirkungen kommen, wenn wir eine ForEach-Schleife mit async/await verwenden. Betrachten Sie das folgende Beispiel. Die Anweisung console.log wartet hier nicht auf „await“

grüße(Name).
asynchrone Funktion greet(Name) {
 return Promise.resolve(`Hallo ${name}, wie geht es dir?`);
}

(Funktion() {
  console.log("vor dem Drucken von Namen");
  Konstantennamen = ['John', 'Jane', 'Joe'];
  Namen.fürJeden(async (Name) => {
   //wartet hier nicht
    console.log(warte auf Begrüßung(Name));
  });
  console.log("nach dem Drucken von Namen");
})();
/*
vor dem Drucken von Namen
nach dem Drucken von Namen
Hallo John, wie geht es dir?
Hallo Jane, wie geht es dir?
Hallo Joe, wie geht es dir?
*/

Mehr als nur syntaktischer Zucker

Bisher wissen wir nur, dass async/await unseren Code lesbarer und debugfreundlicher macht, und manche Leute sagen, es sei eine Art Syntaxvereinfachung für JavaScript-Promises. Eigentlich ist es mehr als nur syntaktischer Zucker.

// versprechen
async1()
.then(x => asyncTwo(x))
.then(y => asyncThree(y))
//andere Anweisung
console.log("hallo")


//asynchron warten
x = warte auf async1();
y = warte auf asyncTwo(x);
warte auf asyncThree(y);

„await“ unterbricht die Ausführung der aktuellen Funktion, während „promise“ die Ausführung der aktuellen Funktion fortsetzt und den Wert zu „then()“ hinzufügt. Zwischen den beiden Durchführungsarten bestehen erhebliche Unterschiede.

Lassen Sie es mich erklären. Betrachten Sie die Promise-Version. Wenn asyncTwo() oder asyncThree() beim Ausführen der Aufgabe einen asynchronen Fehler auslösen, wird dann async1() in den Stacktrace aufgenommen?

Hier unterbricht das Promise die Ausführung der aktuellen Funktion nicht. Wenn asyncTwo es auflöst oder ablehnt, liegt der Kontext nicht innerhalb der Promise-Anweisung. Daher darf „asyncOne“ im Idealfall nicht im Stacktrace enthalten sein. Aber dank der V8-Engine kann hier etwas Magisches geschehen und asyncOne() wird in den Kontext aufgenommen, indem im Voraus darauf verwiesen wird. Aber es ist nicht kostenlos. Das Erfassen eines Stacktraces nimmt Zeit in Anspruch (d. h. es verringert die Leistung). Das Speichern dieser Stapeltraces erfordert Speicher.

Hier übertrifft async/await Promises in puncto Leistung, da die Ausführung der aktuellen Funktion angehalten wird, bis die erwartete Funktion abgeschlossen ist, sodass wir bereits über einen Verweis auf diese Funktion verfügen.

Zusammenfassen

Dies ist das Ende dieses Artikels über asynchrones Warten in Javascript. Weitere relevante Inhalte zum asynchronen Warten in Javascript finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Über die Verwendung und das Verständnis von async/await in JavaScript

<<:  Einfache Zusammenfassung der Methoden zur Leistungsoptimierung von Tomcat

>>:  Erläuterung des MySQL-Multitabellen-Join-Abfragebeispiels

Artikel empfehlen

Implementierung der Installation und Deinstallation von CUDA und CUDNN in Ubuntu

Inhaltsverzeichnis Vorwort Installieren des Grafi...

So stellen Sie mit Node-Red eine Verbindung zur MySQL-Datenbank her

Um Node-red mit der Datenbank (mysql) zu verbinde...

NestJs verwendet Mongoose zum Betrieb von MongoDB

Ich habe vor Kurzem angefangen, das NestJs-Framew...

Lösung für das 404-Problem der Tomcat-Installation in Docker

Suchen Sie die Container-ID von Tomcat und rufen ...

So testen Sie die maximale Anzahl von TCP-Verbindungen in Linux

Vorwort Es besteht ein Missverständnis bezüglich ...

Drei Strategien zum Umschreiben von MySQL-Abfrageanweisungen

Inhaltsverzeichnis Komplexe Abfrage und schrittwe...

Entwicklungsdetails von Vue3-Komponenten

Inhaltsverzeichnis 1. Einleitung 2. Komponentenen...

So ändern Sie das Passwort in MySQL 5.7.18

So ändern Sie das Passwort in MySQL 5.7.18: 1. Fa...

Tiefgreifendes Verständnis der logischen Architektur von MySQL

MySQL ist heute die Datenbank, die von den meiste...

Detaillierte Erklärung des Workbench-Beispiels in MySQL

MySQL Workbench – Modellierungs- und Designtool 1...