Node.js Callback-Funktionen

Node.js ist eine JavaScript-Laufzeitumgebung, die auf der Chrome V8-Engine basiert. Sie ermöglicht es JavaScript, serverseitig unabhängig vom Browser ausgeführt zu werden.

Eine der Kernfunktionen von Node.js ist sein nicht blockierendes I/O-Modell (Eingabe/Ausgabe), das Node.js sehr geeignet für die Verarbeitung von Netzwerkanwendungen mit hoher Parallelität macht.

Callbacks sind die direkte Verkörperung der asynchronen Programmierung von Node.js.

In Node.js ist eine Callback-Funktion ein asynchrones Programmiermuster, das verwendet wird, um I/O-Operationen wie das Lesen und Schreiben von Dateien, Datenbankinteraktionen und Netzwerkanfragen zu verarbeiten. Diese Operationen dauern normalerweise lange; wenn sie synchron behandelt würden, würden sie die Ausführung des gesamten Programms blockieren, bis die Operation abgeschlossen ist.

Mit Callback-Funktionen kann Node.js anderen Code weiter ausführen, während eine I/O-Operation läuft, und dann den Callback ausführen, sobald die I/O-Operation abgeschlossen ist.

Node.js ist single-threaded, aber es erreicht asynchrone Operationen durch einen ereignisgesteuerten und Callback-Mechanismus.

Anwendungsfälle

  • I/O-Operationen wie das Lesen und Schreiben von Dateien.
  • Verarbeitung von Netzwerkanfragen.
  • Datenbankabfragen.

Zum Beispiel können wir eine Datei lesen und gleichzeitig andere Befehle ausführen. Wenn das Lesen der Datei abgeschlossen ist, geben wir den Dateiinhalt als Parameter der Callback-Funktion zurück. Auf diese Weise gibt es beim Ausführen von Code keine Blockierung oder Wartezeit für Datei-I/O-Operationen. Dies verbessert die Leistung von Node.js erheblich und ermöglicht die Verarbeitung einer großen Anzahl gleichzeitiger Anfragen.

Callback-Funktionen erscheinen im Allgemeinen als letzter Parameter einer Funktion:

function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }

Beispiel

Beispiel für blockierenden Code

Erstellen Sie eine Datei input.txt mit folgendem Inhalt:

Example官网地址:www.example.com

Erstellen Sie eine Datei main.js mit folgendem Code:

Beispiel

var fs = require("fs");

var data = fs.readFileSync('input.txt');

console.log(data.toString());
console.log("Programmausführung beendet!");

Das Ausführungsergebnis des obigen Codes ist wie folgt:

$ node main.js
Example官网地址:www.example.com

程序执行结束!

Beispiel für nicht blockierenden Code

Erstellen Sie eine Datei input.txt mit folgendem Inhalt:

Example官网地址:www.example.com

Erstellen Sie eine Datei main.js mit folgendem Code:

Beispiel

var fs = require("fs");

fs.readFile('input.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});

console.log("Programmausführung beendet!");

Parametererklärung:

  • errEs ist das Fehlerobjekt, und es wird einen Wert haben, wenn das Lesen fehlschlägt.
  • dataEs ist der Dateiinhalt, der gelesen wurde.

Das Ausführungsergebnis des obigen Codes ist wie folgt:

$ node main.js
程序执行结束!
Example官网地址:www.example.com

Anhand der beiden obigen Beispiele lässt sich der Unterschied zwischen blockierenden und nicht blockierenden Aufrufen erkennen:

  • Das erste Beispiel führt das Programm erst aus, nachdem das Lesen der Datei abgeschlossen ist.

  • Im zweiten Beispiel müssen wir nicht warten, bis das Lesen der Datei abgeschlossen ist, sodass wir während des Lesens der Datei nachfolgenden Code ausführen können, was die Programmleistung erheblich verbessert.

Daher erfolgt die blockierende Ausführung sequenziell, während nicht blockierende Aufrufe keine sequenzielle Reihenfolge erfordern. Wenn Sie also die Parameter der Callback-Funktion verarbeiten müssen, müssen Sie dies innerhalb der Callback-Funktion schreiben.


Callback-Hölle

Wenn mehrere asynchrone Operationen nacheinander ausgeführt werden müssen, führen Callback-Funktionen zu verschachteltem Code, wodurch der Code schwer zu lesen und zu warten ist.

Beispiel

fs.readFile('file1.txt', 'utf8', (err, data1) => {
    if (err) {
        console.error('Fehler beim Lesen von file1:', err);
        return;
    }

    fs.readFile('file2.txt', 'utf8', (err, data2) => {
        if (err) {
            console.error('Fehler beim Lesen von file2:', err);
            return;
        }

        fs.readFile('file3.txt', 'utf8', (err, data3) => {
            if (err) {
                console.error('Fehler beim Lesen von file3:', err);
                return;
            }

            console.log('Daten aus allen Dateien:', data1, data2, data3);
        });
    });
});

Um die Lesbarkeit und Wartbarkeit des Codes zu verbessern, können Sie die folgenden Methoden verwenden:

  • Promises: Promises sind ein neues asynchrones Programmiermuster, das es ermöglicht, asynchrone Operationen auf verkettbare Weise zu behandeln und die Callback-Hölle zu vermeiden.
  • async/await: Basierend auf Promises,async/awaitEs bietet einen asynchronen Programmierstil, der näher an synchronem Code ist, wodurch asynchroner Code leichter zu verstehen und zu warten ist.
  • Ereignisgesteuerte Programmierung: Node.js unterstützt auch ereignisgesteuerte Programmierung, indem es Ereignisse abhört und auslöst, um asynchrone Operationen zu behandeln.

1. async/await verwenden

async/await ist syntaktischer Zucker, der in ES2017 eingeführt wurde und es ermöglicht, asynchrone Operationen bequemer zu handhaben und die Callback-Hölle zu vermeiden.

Beispiel

const fs = require('fs').promises;

async function readFiles() {
    try {
        const data1 = await fs.readFile('file1.txt', 'utf8');
        const data2 = await fs.readFile('file2.txt', 'utf8');
        const data3 = await fs.readFile('file3.txt', 'utf8');

        console.log('Daten aus allen Dateien:', data1, data2, data3);
    } catch (err) {
        console.error('Error reading files:', err);
    }
}

readFiles();

. Promises verwenden

Promises sind eine weitere Möglichkeit, asynchrone Operationen zu handhaben. Du kannst Aufrufe an die then-Methode verketten, um verschachtelte Callbacks zu vermeiden.

Beispiel

const fs = require('fs').promises;

fs.readFile('file1.txt', 'utf8')
    .then(data1 => {
        console.log('Daten aus Datei 1:', data1);
        return fs.readFile('file2.txt', 'utf8');
    })
    .then(data2 => {
        console.log('Daten aus Datei 2:', data2);
        return fs.readFile('file3.txt', 'utf8');
    })
    .then(data3 => {
        console.log('Daten aus Datei 3:', data3);
    })
    .catch(err => {
        console.error('Error reading files:', err);
    });

Callback-Funktionen sind eine grundlegende Möglichkeit, asynchrone Operationen in Node.js zu behandeln, aber mit zunehmender Komplexität der Anwendung wird das Problem der Callback-Hölle immer offensichtlicher. Durch die Verwendung von async/await oder Promises können die Lesbarkeit und Wartbarkeit des Codes erheblich verbessert werden.

Andere Erweiterungen