Suche…


Einführung

async und await bauen auf Versprechungen und Generatoren auf, um asynchrone Aktionen inline auszudrücken. Dies macht die Pflege von asynchronem Code oder Callback-Code wesentlich einfacher.

Funktionen mit dem async Schlüsselwort geben ein Promise und können mit dieser Syntax aufgerufen werden.

Innerhalb einer async function das await Schlüsselwort auf ein beliebiges Promise angewendet werden und bewirkt, dass der gesamte Funktionsrumpf nach dem await ausgeführt wird, nachdem das Versprechen aufgelöst wurde.

Syntax

  • Async-Funktion foo () {
    ...
    Erwarte asyncCall ()
    }
  • async-Funktion () {...}
  • async () => {...}
  • (async () => {
    const data = asyncCall () erwarten
    console.log (data)}) ()

Bemerkungen

Async-Funktionen sind ein syntaktischer Zucker über Versprechungen und Generatoren. Sie helfen Ihnen dabei, Ihren Code lesbarer, wartbarer zu machen, Fehler leichter zu erkennen und weniger Einzug zu haben.

Einführung

Eine Funktion wie definiert async ist eine Funktion , die asynchrone Aktionen ausführen können , aber immer noch synchron aussehen. Auf diese Weise wird die Funktion mit dem Schlüsselwort await , während sie darauf wartet, dass ein Versprechen aufgelöst oder abgelehnt wird.

Hinweis: Async-Funktionen sind ein Vorschlag der Stufe 4 ("Abgeschlossen"), der auf dem richtigen Weg in den ECMAScript-Standard 2017 aufgenommen werden soll.

Verwenden Sie beispielsweise die promisebasierte Fetch-API :

async function getJSON(url) {
    try {
        const response = await fetch(url);
        return await response.json();
    }
    catch (err) {
        // Rejections in the promise will get thrown here
        console.error(err.message);
    }
}

Eine asynchrone Funktion gibt immer ein Promise selbst zurück, sodass Sie es in anderen asynchronen Funktionen verwenden können.

Pfeil funktion stil

const getJSON = async url => {
    const response = await fetch(url);
    return await response.json();
}

Weniger Einzug

Mit Versprechen:

function doTheThing() {
    return doOneThing()
        .then(doAnother)
        .then(doSomeMore)
        .catch(handleErrors)
}

Bei asynchronen Funktionen:

async function doTheThing() {
    try {
        const one = await doOneThing();
        const another = await doAnother(one);
        return await doSomeMore(another);
    } catch (err) {
        handleErrors(err);
    }
}

Beachten Sie, wie sich die Rückgabe unten und nicht oben befindet, und Sie verwenden die systemeigenen Mechanismen zur Fehlerbehandlung ( try/catch ) der Sprache.

Warten Sie und Vorrang des Bedieners

Sie müssen die Operator-Vorrangigkeit berücksichtigen, wenn Sie das await Schlüsselwort verwenden.

Stellen Sie sich vor, wir haben eine asynchrone Funktion, die eine weitere asynchrone Funktion getUnicorn() , getUnicorn() die eine Promise zurückgibt, die in eine Instanz der Klasse Unicorn . Nun wollen wir die Größe des Einhorns mit der getSize() Methode dieser Klasse ermitteln.

Schauen Sie sich den folgenden Code an:

async function myAsyncFunction() {
    await getUnicorn().getSize();
}

Auf den ersten Blick scheint es gültig zu sein, ist es aber nicht. Aufgrund der Bedienerpriorität entspricht dies dem Folgenden:

async function myAsyncFunction() {
    await (getUnicorn().getSize());
}

Hier versuchen wir, die Methode getSize() des Promise-Objekts getSize() , was wir nicht wollen.

Stattdessen sollten wir Klammern verwenden, um getSize() dass wir zuerst auf das Einhorn warten möchten, und dann die Methode getSize() des Ergebnisses aufrufen:

async function asyncFunction() {
    (await getUnicorn()).getSize();
}

Na sicher. Die vorherige Version könnte in einigen Fällen gültig sein, z. B. wenn die getUnicorn() Funktion synchron war, die getSize() Methode jedoch asynchron war.

Async-Funktionen im Vergleich zu Versprechen

async ersetzen nicht den Promise Typ. Sie fügen Sprachschlüsselwörter hinzu, die das Aufrufen von Versprechen erleichtern. Sie sind austauschbar:

async function doAsyncThing() { ... }

function doPromiseThing(input) { return new Promise((r, x) => ...); }

// Call with promise syntax
doAsyncThing()
    .then(a => doPromiseThing(a))
    .then(b => ...)
    .catch(ex => ...);

// Call with await syntax
try {
    const a = await doAsyncThing();
    const b = await doPromiseThing(a);
    ...
}
catch(ex) { ... }

Jede Funktion, die Ketten von Versprechen verwendet, kann mit await neu geschrieben werden:

function newUnicorn() {
  return fetch('unicorn.json')                     // fetch unicorn.json from server
  .then(responseCurrent => responseCurrent.json()) // parse the response as JSON
  .then(unicorn =>
    fetch('new/unicorn', {                         // send a request to 'new/unicorn' 
        method: 'post',                            // using the POST method
        body: JSON.stringify({unicorn})            // pass the unicorn to the request body
    })
  )
  .then(responseNew => responseNew.json())
  .then(json => json.success)                      // return success property of response
  .catch(err => console.log('Error creating unicorn:', err));
 }

Die Funktion kann mit async / await wie folgt async werden:

async function newUnicorn() {
  try {
    const responseCurrent = await fetch('unicorn.json'); // fetch unicorn.json from server
    const unicorn = await responseCurrent.json();        // parse the response as JSON
    const responseNew = await fetch('new/unicorn', {     // send a request to 'new/unicorn'
      method: 'post',                                    // using the POST method
      body: JSON.stringify({unicorn})                    // pass the unicorn to the request body
    });
    const json = await responseNew.json();
    return json.success                                  // return success property of response
  } catch (err) {
    console.log('Error creating unicorn:', err);
  }
}

Diese async Variante von newUnicorn() scheint ein Promise , aber tatsächlich gab es mehrere await Schlüsselwörter. Jeder gab ein Promise , also hatten wir wirklich eine Sammlung von Versprechungen und keine Kette.

In der Tat können wir es als function* vorstellen, wobei jedes await ein yield new Promise . Die Ergebnisse jedes Versprechens werden jedoch von den nächsten benötigt, um die Funktion fortzusetzen. Aus diesem Grund wird das zusätzliche Schlüsselwort async für die Funktion benötigt (sowie das await Schlüsselwort beim Aufruf der Versprechungen), da Javascript dazu aufgefordert wird, automatisch einen Beobachter für diese Iteration zu erstellen. Die von async function newUnicorn() Promise async function newUnicorn() wenn diese Iteration abgeschlossen ist.

In der Praxis müssen Sie das nicht berücksichtigen. await verbirgt das Versprechen, und async verbirgt die Iteration des Generators.

Sie können async Funktionen aufrufen, als wären sie Versprechungen, und await ein Versprechen oder eine async Funktion await . Sie müssen nicht await eine asynchrone Funktion await , genauso wie Sie ein Versprechen ohne .then() ausführen können.

Sie können auch eine Verwendung async IIFE , wenn Sie sofort , dass Code ausgeführt werden sollen:

(async () => {
  await makeCoffee()
  console.log('coffee is ready!')
})()

Schleifen mit Async erwarten

Wenn Sie in Schleifen async erwarten, können einige dieser Probleme auftreten.

Wenn Sie nur versuchen , im Inneren warten zu verwenden forEach , wird dies einen werfen Unexpected token Fehler.

(async() => {
 data = [1, 2, 3, 4, 5];
 data.forEach(e => {
   const i = await somePromiseFn(e);
   console.log(i);
 });
})();

Dies ist darauf zurückzuführen, dass Sie die Pfeilfunktion irrtümlicherweise als Block gesehen haben. Das await erfolgt im Kontext der Rückruffunktion, die nicht async .
Der Interpreter schützt uns davor, den obigen Fehler zu machen, aber wenn Sie den forEach Rückruf async hinzufügen, werden keine Fehler ausgegeben. Sie denken vielleicht, dass dies das Problem löst, aber es funktioniert nicht wie erwartet.

Beispiel:

(async() => {
  data = [1, 2, 3, 4, 5];
  data.forEach(async(e) => {
    const i = await somePromiseFn(e);
    console.log(i);
  });
  console.log('this will print first');
})();

Dies geschieht, weil die asynchrone Callback-Funktion nur sich selbst anhalten kann und nicht die übergeordnete async-Funktion.

Sie könnten eine asyncForEach-Funktion schreiben, die ein Versprechen zurückgibt, und dann könnten Sie so etwas wie await asyncForEach(async (e) => await somePromiseFn(e), data ) . Es gibt jedoch bessere Möglichkeiten, dies zu tun, und zwar einfach eine Schleife zu verwenden.

Sie können eine for-of Schleife oder eine for/while Schleife verwenden. Dabei ist es egal, welche Sie auswählen.

(async() => {
  data = [1, 2, 3, 4, 5];
  for (let e of data) {
    const i = await somePromiseFn(e);
    console.log(i);
  }
  console.log('this will print last');
})();

Aber es gibt noch einen anderen Haken. Diese Lösung wartet, bis der Aufruf von somePromiseFn abgeschlossen ist, bevor der nächste somePromiseFn .
Dies ist großartig, wenn Sie möchten, dass die somePromiseFn der somePromiseFn nach ausgeführt werden. Wenn Sie jedoch gleichzeitig ausführen möchten, müssen Sie auf Promise.all await .

(async() => {
 data = [1, 2, 3, 4, 5];
 const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
 console.log(...p);
})();

Promise.all erhält eine Reihe von Versprechen als einzigen Parameter und gibt ein Versprechen zurück. Wenn alle Versprechen im Array gelöst sind, wird auch das zurückgegebene Versprechen gelöst. Wir await dieses Versprechen und wenn es gelöst ist, stehen alle unsere Werte zur Verfügung.

Die obigen Beispiele sind voll lauffähig. Die somePromiseFn Funktion kann als somePromiseFn mit Timeout ausgeführt werden. Sie können die Beispiele in babel-repl mit mindestens dem stage-3 Preset ausprobieren und sich die Ausgabe ansehen.

function somePromiseFn(n) {
 return new Promise((res, rej) => {
   setTimeout(() => res(n), 250);
 });
}

Gleichzeitige asynchrone (parallele) Operationen

Oft möchten Sie asynchrone Operationen parallel ausführen. Es gibt eine direkte Syntax, die dies im Vorschlag für async / await unterstützt. Da await jedoch auf ein Versprechen wartet, können Sie mehrere Zusagen in Promise.all , um Promise.all zu warten:

// Not in parallel

async function getFriendPosts(user) {
    friendIds = await db.get("friends", {user}, {id: 1});
    friendPosts = [];
    for (let id in friendIds) {
        friendPosts = friendPosts.concat( await db.get("posts", {user: id}) );
    }
    // etc.
}

Dies führt jede Abfrage aus, um die Beiträge jedes Freundes seriell abzurufen, sie können jedoch gleichzeitig durchgeführt werden:

// In parallel

async function getFriendPosts(user) {
    friendIds = await.db.get("friends", {user}, {id: 1});
    friendPosts = await Promise.all( friendIds.map(id => 
      db.get("posts", {user: id})
    );
    // etc.
}

Dadurch wird die Liste der IDs durchlaufen, um eine Reihe von Versprechen zu erstellen. await bis alle Versprechen vollständig sind. Promise.all kombiniert sie zu einem einzigen Versprechen, aber sie werden parallel ausgeführt.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow