Tutorial

So schreiben Sie asynchronen Code in Node.js

Published on April 24, 2020
Deutsch
So schreiben Sie asynchronen Code in Node.js

Der Autor hat den Open Internet/Free Speech Fund dazu ausgewählt, eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

Einführung

Bei vielen Programmen in JavaScript wird Code ausgeführt, während der Entwickler ihn schreibt – Zeile für Zeile. Dies wird synchrone Ausführung genannt, da die Zeilen nacheinander in der Reihenfolge ausgeführt werden, in der sie geschrieben wurden. Jedoch muss nicht jeden Befehl, den Sie dem Computer geben, sofort beachtet werden. Wenn Sie beispielsweise eine Netzwerkanfrage senden, muss der Prozess, der Ihren Code ausführt, darauf warten, dass die Daten zurückkehren, bevor er damit arbeiten kann. In diesem Fall würde Zeit verschwendet werden, wenn er keinen anderen Code ausführen würde, während er darauf wartet, dass die Netzwerkanfrage abgeschlossen wird. Um dieses Problem zu lösen, verwenden Entwickler asynchrone Programmierung, wobei Codezeilen in einer anderen Reihenfolge ausgeführt werden als in jener, in der sie geschrieben wurden. Bei asynchroner Programmierung können wir einen anderen Code ausführen, während wir darauf warten, dass lange Aktivitäten wie Netzwerkanfragen abgeschlossen werden.

JavaScript-Code wird in einem einzelnen Thread innerhalb eines Computer-Prozesses ausgeführt. Sein Code wird auf diesem Thread synchron verarbeitet, wobei immer nur ein Befehl ausgeführt wird. Wenn wir daher eine zeitintensive Aufgabe auf diesem Thread ausführen, wird der gesamte verbleibende Code blockiert, bis die Aufgabe abgeschlossen ist. Indem wir uns JavaScripts asynchrone Programmierung zunutze machen, können wir zeitintensive Aufgaben zu einem Hintergrund-Thread auslagern, um dieses Problem zu vermeiden. Wenn die Aufgabe abgeschlossen ist, wird der Code, den wir für die Datenverarbeitung dieser Aufgabe benötigen, wieder zum einzelnen Haupt-Thread übertragen.

In diesem Tutorial lernen Sie, wie JavaScript asynchrone Aufgaben mithilfe von Event Loop verwaltet. Dies ist ein JavaScript-Konstrukt, das eine neue Aufgabe ausführt, während es auf eine andere wartet. Dann erstellen Sie ein Programm, das mit asynchroner Programmierung eine Filmliste von einer Studio Ghibli API​​​ anfordert und die Daten in einer CSV-Datei speichert. Der asynchrone Code wird auf drei Arten geschrieben: Callbacks, Promises und mit den Schlüsselwörtern async/await.

Anmerkung: Seitdem dieser Beitrag verfasst wurde, wird asynchrone Programmierung nicht mehr nur mit Callbacks durchgeführt. Wenn man jedoch diese veraltete Methode lernt, versteht man besser, warum die JavaScript-Community jetzt Promises verwendet. Die Schlüsselwörter async/await ermöglichen es uns, Promises auf eine weniger ausführliche Weise zu verwenden, und sind daher die standardmäßige Art der asynchronen Programmierung in JavaScript zur Zeit der Veröffentlichung dieses Artikels.

Voraussetzungen

Die Ereignisschleife

Beginnen wir mit den internen Abläufen der Funktionsausführung von JavaScript. Dies zu verstehen ermöglicht es Ihnen, asynchronen Code gezielt zu schreiben, und es hilft Ihnen in Zukunft bei der Code-Problembehebung.

Da JavaScript Interpreter den Code ausführt, wird jede Funktion, die aufgerufen wird, dem Call stack von JavaScript hinzugefügt. Der Call stack ist ein Stapel – eine listenähnliche Datenstruktur, bei der Elemente nur oben hinzugefügt und entfernt werden können. Stacks folgen dem Prinzip „Last in, first out“ (LIFO-Prinzip). Wenn Sie dem Stapel zwei Einträge hinzufügen, wird das zuletzt hinzugefügte Element zuerst entfernt.

Wir illustrieren es mit einem Beispiel mit dem Call stack. Wenn JavaScript auf eine functionA() trifft, die aufgerufen wird, wird sie dem Call stack hinzugefügt. Wenn diese Funktion functionA() eine andere Funktion functionB() aufruft, wird functionB() dem Call stack oben hinzugefügt. Da JavaScript die Ausführung einer Funktion abgeschlossen hat, wird sie vom Call stack entfernt. Daher führt JavaScript functionB() zuerst aus, löscht sie aus dem Stapel, wenn sie abgeschlossen ist und schließt die Ausführung von functionA() ab und entfernt sie aus dem Call stack. Aus diesem Grund werden innere Funktionen immer vor ihren äußeren Funktionen ausgeführt.

Wenn JavaScript auf eine asynchrone Operation trifft, wie z. B. in eine Datei schreiben, fügt sie diese zu einer Tabelle in ihrem Speicher hinzu. Diese Tabelle speichert die Operation, die Bedingung, um sie zu abzuschließen, und die Funktion, die nach Fertigstellung aufgerufen wird. Wenn die Operation abgeschlossen ist, fügt JavaScript die verknüpfte Funktion der Message Queue hinzu. Eine Warteschlange (Queue) ist eine weitere listenartige Datenstruktur, bei der Einträge nur unten hinzugefügt, jedoch von oben entfernt werden können. Wenn zwei oder mehr asynchrone Operationen in der Message Queue bereit für die Ausführung ihrer Funktionen sind, wird die Funktion der asynchronen Operation, die zuerst abgeschlossen wurde, zuerst für die Ausführung markiert.

Funktionen in der Message Queue warten, dass sie dem Call stack hinzugefügt werden. Die Ereignisschleife ist ein fortlaufender Vorgang, der überprüft, ob der Call stack leer ist. Wenn das der Fall ist, wird der erste Eintrag in der Message Queue in den Call stack verschoben. JavaScript priorisiert Funktionen in der Message Queue gegenüber Funktions-Calls, die es im Code interpretiert. Der kombinierte Effekt des Call stacks, der Message Queue und Ereignisschleife ermöglicht die Verarbeitung von JavaScript-Code bei der Verwaltung asynchroner Aktivitäten.

Nachdem Sie sich jetzt einen Überblick darüber verschafft haben, wie die Ereignisschleife funktioniert, wissen Sie, wie der asynchrone Code, den Sie schreiben, ausgeführt wird. Mit diesem Wissen können Sie jetzt asynchronen Code mit drei verschiedenen Ansätzen erstellen: Callbacks, Promises und async/await.

Asynchrone Programmierung mit Callbacks

Eine Callback-Funktion ist eine Funktion, die als Argument einer anderen Funktion übergeben und dann ausgeführt wird, wenn die andere Funktion abgeschlossen ist. Wir verwenden Callbacks, um sicherzustellen, dass Code nur ausgeführt wird, wenn eine asynchrone Operation abgeschlossen ist.

Callbacks waren lange Zeit der gängigste Mechanismus für das Schreiben von asynchronem Code, aber jetzt sind sie weitgehend veraltet, da sie das Lesen des Codes erschweren können. In diesem Schritt schreiben Sie ein Beispiel für asynchronen Code mit Callbacks, damit Sie ihn als Grundwert verwenden können, um die gesteigerte Effizienz anderer Strategien zu sehen.

Es gibt viele Möglichkeiten, um Callback-Funktionen in einer anderen Funktion zu verwenden. In der Regel haben sie diese Struktur:

function asynchronousFunction([ Function Arguments ], [ Callback Function ]) {
    [ Action ]
}

Obwohl JavaScript oder Node.js es syntaktisch nicht erfordert, dass die Callback-Funktion das letzte Argument der äußeren Funktion ist, ist es eine gängige Praxis, die das Identifizieren von Callbacks erleichtert. JavaScript-Entwickler verwenden oft auch eine anonyme Funktion als Callback. Anonyme Funktionen sind solche, die ohne Namen erstellt werden. Es ist in der Regel viel lesbarer, wenn eine Funktion am Ende der Argumentliste definiert wird.

Um Callbacks zu demonstrieren, erstellen wir ein Node.js-Modul, das eine Liste aus Filmen von Studio Ghibli in eine Datei schreibt. Erstellen Sie zunächst einen Ordner, in dem unsere JavaScript-Datei und ihre Ausgabe gespeichert werden:

  1. mkdir ghibliMovies

Gehen Sie dann in diesen Ordner hinein:

  1. cd ghibliMovies

Wir beginnen mit einer HTTP-Anfrage an die Studio Ghibli API, deren Ergebnisse unsere Callback-Funktion protokolliert. Dazu installieren wir eine Bibliothek, die es uns ermöglicht, auf die Daten einer HTTP-Antwort in einem Callback zuzugreifen.

Initialisieren Sie npm in Ihrem Terminal, damit wir später eine Referenz für unsere Pakete haben:

  1. npm init -y

Installieren Sie dann die Bibliothek request:

  1. npm i request --save

Öffnen Sie nun eine neue Datei namens callbackMovies.js in einem Texteditor wie nano:

  1. nano callbackMovies.js

Geben Sie den folgenden Code in Ihren Texteditor ein. Zuerst senden wir eine HTTP-Anfrage mit dem Modul request:

callbackMovies.js
const request = require('request');

request('https://ghibliapi.herokuapp.com/films');

In der ersten Zeile laden wir das Modul request, das über npm installiert wurde. Das Modul gibt eine Funktion zurück, die HTTP-Anfragen vornehmen kann. Diese Funktion speichern wir dann in der Konstante request.

Danach erstellen wir die HTTP-Anfrage mit der Funktion request(). Drucken wir nun die Daten von der HTTP-Anfrage an die Konsole, indem wir die hervorgehobenen Änderungen hinzufügen:

callbackMovies.js
const request = require('request');

request('https://ghibliapi.herokuapp.com/films', (error, response, body) => {
    if (error) {
        console.error(`Could not send request to API: ${error.message}`);
        return;
    }

    if (response.statusCode != 200) {
        console.error(`Expected status code 200 but received ${response.statusCode}.`);
        return;
    }

    console.log('Processing our list of movies');
    movies = JSON.parse(body);
    movies.forEach(movie => {
        console.log(`${movie['title']}, ${movie['release_date']}`);
    });
});

Wenn wir die Funktion request() verwenden, geben wir ihr zwei Parameter:

  • Die URL der Website, die wir anfragen
  • Eine Callback-Funktion, die Fehler oder erfolgreiche Antworten behandelt, nachdem die Anfrage abgeschlossen ist.

Unsere Callback-Funktion verfügt über drei Argumente: error, response und body. Wenn die HTTP-Anfrage abgeschlossen ist, erhalten die Argumente automatisch Werte, die vom Ergebnis abhängen. Wenn das Senden der Anfrage fehlgeschlagen ist, würde error ein Objekt enthalten, aber response und body wären null. Wenn die Anfrage erfolgreich war, wird die HTTP-Antwort in response gespeichert. Wenn unsere HTTP-Antwort Daten zurückgibt (in diesem Beispiel erhalten wir JSON), dann sind die Daten in body festgelegt.

Unsere Callback-Funktion prüft zunächst, ob wir einen Fehler erhalten. Am besten ist es, einen Callback zuerst auf Fehler zu überprüfen, damit die Ausführung des Callback nicht mit fehlenden Daten fortgesetzt wird. In diesem Fall protokollieren wir den Fehler und die Ausführung der Funktion. Dann überprüfen wir den Statuscode der Antwort. Unser Server ist vielleicht nicht immer verfügbar und APIs können sich ändern, wodurch einst sinnvolle Anfragen fehlerhaft werden. Indem Sie überprüfen, ob der Statuscode 200 ist – was bedeutet, dass die Anfrage „OK“ war – können Sie sicher sein, dass unsere Antwort unseren Erwartungen entspricht.

Schließlich parsen wir den Body der Antwort an ein Array und durchlaufen jeden Film, um seinen Namen und sein Erscheinungsjahr zu protokollieren.

Nachdem Sie die Datei gespeichert und geschlossen haben, führen Sie dieses Skript aus:

  1. node callbackMovies.js

Sie erhalten folgende Ausgabe:

Output
Castle in the Sky, 1986 Grave of the Fireflies, 1988 My Neighbor Totoro, 1988 Kiki's Delivery Service, 1989 Only Yesterday, 1991 Porco Rosso, 1992 Pom Poko, 1994 Whisper of the Heart, 1995 Princess Mononoke, 1997 My Neighbors the Yamadas, 1999 Spirited Away, 2001 The Cat Returns, 2002 Howl's Moving Castle, 2004 Tales from Earthsea, 2006 Ponyo, 2008 Arrietty, 2010 From Up on Poppy Hill, 2011 The Wind Rises, 2013 The Tale of the Princess Kaguya, 2013 When Marnie Was There, 2014

Wir haben eine Liste von Studio-Ghibli-Filmen mit Ihrem Erscheinungsjahr erhalten. Jetzt schließen wir dieses Programm ab, indem wir die Film-Liste, in die wir uns gerade einloggen, eine Datei schreiben.

Aktualisieren Sie die Datei callbackMovies.js in Ihrem Texteditor, um den folgenden hervorgehobenen Code aufzunehmen, der eine CSV-Datei mit unseren Filmdaten erstellt:

callbackMovies.js
const request = require('request');
const fs = require('fs');

request('https://ghibliapi.herokuapp.com/films', (error, response, body) => {
    if (error) {
        console.error(`Could not send request to API: ${error.message}`);
        return;
    }

    if (response.statusCode != 200) {
        console.error(`Expected status code 200 but received ${response.statusCode}.`);
        return;
    }

    console.log('Processing our list of movies');
    movies = JSON.parse(body);
    let movieList = '';
    movies.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });

    fs.writeFile('callbackMovies.csv', movieList, (error) => {
        if (error) {
            console.error(`Could not save the Ghibli movies to a file: ${error}`);
            return;
        }

        console.log('Saved our list of movies to callbackMovies.csv');;
    });
});

Anhand der hervorgehobenen Änderungen sehen wir, dass wir das Modul fs importieren. Dieses Modul ist der Standard in allen Node.js-Installationen und enthält die Methode writeFile(), die asynchron in eine Datei schreiben kann.

Statt die Daten in der Konsole zu protokollieren, fügen wir sie nun der Zeichenfolgenvariablen movieList hinzu. Dann speichern wir den Inhalt der movieList mit writeFile() in einer neuen Datei – callbackMovies.csv. Schließlich geben wir der Funktion writeFile(), die das Argument error hat, ein Callback. Dadurch können wir Fälle verarbeiten, bei denen wir nicht in der Lage sind, in eine Datei zu schreiben – z. B. wenn der Benutzer, der den node-Prozess ausführt, diese Berechtigungen nicht hat.

Speichern Sie die Datei und führen Sie dieses Node.js-Programm erneut aus mit:

  1. node callbackMovies.js

In Ihrem Ordner ghibliMovies sehen Sie callbackMovies.csv, die folgenden Inhalt hat:

callbackMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

Beachten Sie, dass wir im Callback der HTTP-Anfrage in unsere CSV-Datei schreiben. Sobald der Code in der Callback-Funktion ist, schreibt er erst in die Datei, nachdem die HTTP-Anfrage abgeschlossen wurde. Wenn wir nach der Erstellung unserer CSV-Datei mit einer Datenbank kommunizieren wollten, würden wir eine weitere asynchrone Funktion erstellen, die im Callback von writeFile() abgerufen würde. Je mehr asynchronen Code wir haben, desto mehr Callback-Funktionen müssen geschachtelt werden.

Stellen wir uns vor, dass wir fünf asynchrone Operationen ausführen möchten, wobei jede erst dann ausgeführt werden kann, wenn eine andere abgeschlossen ist. Wenn wir das programmieren würden, hätten wir in etwa Folgendes:

doSomething1(() => {
    doSomething2(() => {
        doSomething3(() => {
            doSomething4(() => {
                doSomething5(() => {
                    // final action
                });
            });
        });
    });
});

Wenn geschachtelte Callbacks viele Zeilen von Code ausführen müssen, werden sie wesentlich komplexer und weniger lesbar. Wenn Ihr JavaScript größer und komplexer wird, macht sich dieser Effekt immer mehr bemerkbar, bis er letztendlich nicht mehr kontrollierbar ist. Aus diesem Grund verwenden Entwickler keine Callbacks mehr für die Handhabung asynchroner Operationen. Um die Syntax unseres asynchronen Codes zu verbessern, können wir stattdessen Promises verwenden.

Verwendung von Promises für präzise asynchrone Programmierung

Ein Promise ist ein JavaScript-Objekt, das irgendwann in der Zukunft einen Wert zurückgeben wird. Asynchrone Funktionen können Promise-Objekte anstatt konkreter Werte zurückgeben. Wenn wir in der Zukunft einen Wert erhalten, bezeichnen wir das Promise (Versprechen) als erfüllt. Wenn wir in der Zukunft einen Fehler erhalten, bezeichnen wir das Promise als abgelehnt. Andernfalls ist das Promise immer noch in Bearbeitung und daher in einem ausstehenden Zustand.

Promises haben in der Regel die folgende Form:

promiseFunction()
    .then([ Callback Function for Fulfilled Promise ])
    .catch([ Callback Function for Rejected Promise ])

Wie in dieser Vorlage gezeigt, verwenden Promises auch Callback-Funktionen. Wir haben eine Callback-Funktion für die Methode then(), die ausgeführt wird, wenn ein Promise erfüllt wird. Außerdem haben wir eine Callback-Funktion für die Methode catch(), um alle Fehler zu bearbeiten, die während der Ausführung des Promise auftreten.

Probieren wir Promise aus, indem wir unser Programm Studio Ghibli neu schreiben, sodass es stattdessen Promises verwendet.

Axios ist ein promise-basierter HTTP-Client für JavaScript, also installieren wir ihn gleich einmal:

  1. npm i axios --save

Erstellen Sie nun mit dem Texteditor Ihrer Wahl die neue Datei promiseMovies.js:

  1. nano promiseMovies.js

Unser Programm erstellt eine HTTP-Anfrage mit axios und verwendet dann eine spezielle promise-basierte Version von fs zur Speicherung in einer neuen CSV-Datei.

Geben Sie diesen Code in promiseMovies.js ein, damit wir Axios laden und eine HTTP-Anfrage an die Film-API senden können:

promiseMovies.js
const axios = require('axios');

axios.get('https://ghibliapi.herokuapp.com/films');

In der ersten Zeile laden wir das Modul axios und speichern die zurückgegebene Funktion in einer Konstanten namens axios. Dann verwenden wir die Methode axios.get(), um eine HTTP-Anfrage an die API zu senden.

Die Methode axios.get() gibt ein Promise zurück. Verketten wir jetzt das Promise, damit wir die Liste der Ghibli-Filme zur Konsole drucken können:

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        response.data.forEach(movie => {
            console.log(`${movie['title']}, ${movie['release_date']}`);
        });
    })

Sehen wir uns genauer an, was passiert. Nach einer HTTP-GET-Anfrage mit axios.get() verwenden wir die Funktion then(), die erst dann ausgeführt wird, wenn das Promise erfüllt ist. In diesem Fall drucken wir die Filme auf den Bildschirm, so wie im Callback-Beispiel.

Fügen Sie zur Verbesserung dieses Programms den hervorgehobenen Code hinzu, um die HTTP-Daten in eine Datei zu schreiben:

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });

        return fs.writeFile('promiseMovies.csv', movieList);
    })
    .then(() => {
        console.log('Saved our list of movies to promiseMovies.csv');
    })

Zusätzlich importieren wir das Modul fs erneut. Sie werden sehen, dass wir nach dem Import von fs .Promises haben. Node.js enthält eine promise-basierte Version der callback-basierten Bibliothek fs, sodass die Abwärtskompatibilität in Legacyprojekten nicht fehlerhaft ist.

Die erste then()-Funktion, die die HTTP-Anfrage verarbeitet, ruft nun fs.writeFile() auf, anstatt zur Konsole zu drucken. Da wir die promise-basierte Version von fs importiert haben, gibt unsere Funktion writeFile() ein weiteres Promise zurück. Daher fügen wir eine andere then()-Funktion hinzu für den Zeitpunkt, wenn das Promise writeFile() erfüllt ist.

Ein Promise kann ein neues Promise zurückgeben, sodass wir ein Promise nach dem anderen ausführen können. Das ebnet uns den Weg für die Ausführung mehrerer synchroner Operationen. Dies wird als Promise-Verkettung bezeichnet und ist analog zur Schachtelung von Callbacks. Die zweite Funktion then() wird erst aufgerufen, nachdem wir erfolgreich in die Datei geschrieben haben.

Anmerkung: In diesem Beispiel haben wir den HTTP-Statuscode nicht wie im Callback-Beispiel überprüft. Standardmäßig erfüllt axios sein Promise nicht, wenn es einen Statuscode erhält, der auf einen Fehler hindeutet. Daher müssen wir es nicht mehr validieren.

Um dieses Programm abzuschließen, verketten Sie das Promise mit einer catch()-Funktion, wie im Folgenden hervorgehoben:

promiseMovies.js
const axios = require('axios');
const fs = require('fs').promises;


axios.get('https://ghibliapi.herokuapp.com/films')
    .then((response) => {
        console.log('Successfully retrieved our list of movies');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });

        return fs.writeFile('promiseMovies.csv', movieList);
    })
    .then(() => {
        console.log('Saved our list of movies to promiseMovies.csv');
    })
    .catch((error) => {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    });

Wenn ein Promise in der Kette von Promises nicht erfüllt ist, geht JavaScript automatisch zu der catch()-Funktion, wenn diese definiert wurde. Aus diesem Grund haben wir nur eine catch()-Klausel, obwohl wir zwei asynchrone Operationen haben.

Bestätigen wir, dass unser Programm dieselbe Ausgabe erzeugt, indem wir Folgendes ausführen:

  1. node promiseMovies.js

In Ihrem Ordner ghibliMovies sehen Sie die Datei promiseMovies.csv mit:

promiseMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

Mit Promises können wir viel präziseren Code als nur mit Callbacks schreiben. Die Promise-Kette der Callbacks ist eine sauberere Option als die Schachtelung von Callbacks. Wenn wir jedoch mehr asynchrone Calls vornehmen, wird unsere Promise-Kette länger und die Aufrechterhaltung schwieriger.

Die Ausführlichkeit der Callbacks und Promises kommt von der Notwendigkeit, Funktionen zu erstellen, wenn wir das Ergebnis einer asynchronen Aufgabe haben. Besser wäre es, auf ein asynchrones Ergebnis zu warten und es in eine Variable außerhalb der Funktion zu legen. Auf diese Weise können wir die Ergebnisse in den Variablen verwenden, ohne eine Funktion erstellen zu müssen. Dies können wir mit den Schlüsselwörtern async und await erreichen.

JavaScript mit async/await schreiben

Die Schlüsselwörter async/await bieten eine alternative Syntax bei der Arbeit mit Promises. Anstatt das Ergebnis eines Promise in der Methode then() zur Verfügung zu stellen, wird das Ergebnis als Wert wie in jeder anderen Funktion zurückgegeben. Wir definieren eine Funktion mit dem Schlüsselwort async, um JavaScript mitzuteilen, dass es eine asynchrone Funktion ist, die ein Promise zurückgibt. Wir verwenden das Schlüsselwort await, um JavaScript anzuweisen, die Ergebnisse des Promise zurückzugegeben, anstatt das Promise selbst zurückzugeben, wenn es erfüllt ist.

In der Regel sieht die Verwendung async/await folgendermaßen aus:

async function() {
    await [Asynchronous Action]
}

Sehen wir uns an, wie async/await unser Programm Studio Ghibli verbessern kann. Verwenden Sie Ihren Texteditor, um eine neue Datei asyncAwaitMovies.js zu erstellen und zu öffnen:

  1. nano asyncAwaitMovies.js

Zuerst importieren Sie in Ihrer neu geöffneten JavaScript-Datei dieselben Module, die wir in unserem Promise-Beispiel verwendet haben:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

Die Importe sind die gleichen wie promiseMovies.js, da async/await Promises verwendet.

Jetzt verwenden wir das Schlüsselwort async, um eine Funktion mit unserem asynchronen Code zu erstellen:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {}

Wir erstellen eine neue Funktion namens saveMovies(), aber wir schließen async am Anfang seiner Definition ein. Dies ist wichtig, da wir nur das Schlüsselwort await in einer asynchronen Funktion verwenden können.

Verwenden Sie das Schlüsselwort await, um eine HTTP-Anfrage vorzunehmen, die die Liste der Filme von der Ghibli-API erhält:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    let response = await axios.get('https://ghibliapi.herokuapp.com/films');
    let movieList = '';
    response.data.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });
}

In unserer Funktion saveMovies() erstellen wir wie zuvor eine HTTP-Anfrage mit axios.get(). Dieses Mal verbinden wir sie nicht mit einer then()-Funktion. Stattdessen fügen wir await hinzu, bevor sie aufgerufen wird. Wenn JavaScript await sieht, führt es den verbleibenden Code der Funktion erst aus, nachdem axios.get() die Ausführung beendet und die Variable response festgelegt hat. Der andere Code speichert die Filmdaten, damit wir in eine Datei schreiben können.

Schreiben wir die Filmdaten in eine Datei:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    let response = await axios.get('https://ghibliapi.herokuapp.com/films');
    let movieList = '';
    response.data.forEach(movie => {
        movieList += `${movie['title']}, ${movie['release_date']}\n`;
    });
    await fs.writeFile('asyncAwaitMovies.csv', movieList);
}

Wir verwenden das Schlüsselwort await auch, wenn wir mit fs.writeFile() in die Datei schreiben.

Um diese Funktion abzuschließen, müssen wir Fehler erkennen, die unsere Promises auslösen können. Dazu fügen wir unseren Code in einen try/catch-Block ein:

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    try {
        let response = await axios.get('https://ghibliapi.herokuapp.com/films');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });
        await fs.writeFile('asyncAwaitMovies.csv', movieList);
    } catch (error) {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    }
}

Da Promises fehlschlagen können, umhüllen wir unseren asynchronen Code mit einer try/catch-Klausel. Dies erfasst alle Fehler, die bei Fehlschlagen der HTTP-Anfrage oder Dateischreiboperationen ausgelöst werden.

Schließlich rufen wir unsere asynchrone Funktion saveMovies() auf, damit sie ausgeführt wird, wenn wir das Programm mit node ausführen.

asyncAwaitMovies.js
const axios = require('axios');
const fs = require('fs').promises;

async function saveMovies() {
    try {
        let response = await axios.get('https://ghibliapi.herokuapp.com/films');
        let movieList = '';
        response.data.forEach(movie => {
            movieList += `${movie['title']}, ${movie['release_date']}\n`;
        });
        await fs.writeFile('asyncAwaitMovies.csv', movieList);
    } catch (error) {
        console.error(`Could not save the Ghibli movies to a file: ${error}`);
    }
}

saveMovies();

Auf einen Blick sieht dies wie ein typischer synchroner JavaScript-Codeblock aus. Es werden weniger Funktionen durchlaufen, daher sieht es ein wenig ordentlicher aus. Diese kleinen Änderungen erleichtern die Aufrechterhaltung von asynchronem Code mit async/await.

Testen Sie diese Iteration unseres Programms, indem Sie in Ihr Terminal Folgendes eingeben:

  1. node asyncAwaitMovies.js

In Ihrem Ordner ghibliMovies wird eine neue Datei asyncAwaitMovies.csv mit dem folgenden Inhalt erstellt:

asyncAwaitMovies.csv
Castle in the Sky, 1986
Grave of the Fireflies, 1988
My Neighbor Totoro, 1988
Kiki's Delivery Service, 1989
Only Yesterday, 1991
Porco Rosso, 1992
Pom Poko, 1994
Whisper of the Heart, 1995
Princess Mononoke, 1997
My Neighbors the Yamadas, 1999
Spirited Away, 2001
The Cat Returns, 2002
Howl's Moving Castle, 2004
Tales from Earthsea, 2006
Ponyo, 2008
Arrietty, 2010
From Up on Poppy Hill, 2011
The Wind Rises, 2013
The Tale of the Princess Kaguya, 2013
When Marnie Was There, 2014

Sie haben nun die JavaScript-Funktionen async/await für die Verwaltung von asynchronem Code verwendet.

Zusammenfassung

In diesem Tutorial haben Sie gelernt, wie JavaScript die Ausführung von Funktionen handhabt und asynchrone Operationen mit der Ereignisschleife verwaltet. Dann haben Sie Programme geschrieben, die eine CSV-Datei erstellt haben, nachdem Sie mit verschiedenen asynchronen Programmierungstechniken eine HTTP-Anfrage für Filmdaten erstellt haben. Zuerst haben Sie den überholten, auf Callback basierenden Ansatz verwendet. Dann haben Sie Promises und schließlich async/await verwendet, um die Promise-Syntax kurz und bündig zu gestalten.

Mit Ihrem Verständnis für asynchronen Code mit Node.js können Sie nun Programme entwickeln, die von der asynchronen Programmierung profitieren, wie solche, die auf API-Calls angewiesen sind. Sehen Sie sich diese Liste der öffentlichen APIs an. Dazu müssen Sie asynchrone HTTP-Anfragen wie in diesem Tutorial erstellen. Probieren Sie, eine App zu erstellen, die diese APIs verwendet, um die Techniken zu üben, die Sie hier gelernt haben.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors

Default avatar

Senior Technical Editor

Editor at DigitalOcean, fiction writer and podcaster elsewhere, always searching for the next good nautical pun!


Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
1 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Thanks the best tutorial for this topic! :)

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel