Node.js asynchrone Programmierung

In der traditionellensynchronen Programmierungwird Code sequenziell ausgeführt, und jede Codezeile muss warten, bis die vorherige Zeile mit der Ausführung fertig ist, bevor sie ausgeführt werden kann. Allerdingsasynchrone Programmierungermöglicht es dem Code, während des Wartens auf den Abschluss bestimmter Vorgänge (wie E/A-Vorgänge) weiterhin andere Aufgaben auszuführen, ohne die Programmausführung zu blockieren.

Node.js verwendet ein asynchrones E/A-Modell, was es besonders für die Verarbeitung von Netzwerkanwendungen mit hoher Nebenläufigkeit geeignet macht. Das Verständnis der asynchronen Programmierung ist der Schlüssel zur Beherrschung von Node.js.


Warum asynchrone Programmierung in Node.js

Node.js ist single-threaded, was bedeutet, dass es nur eine Aufgabe gleichzeitig ausführen kann. Wenn synchron gearbeitet wird und ein zeitaufwändiger Vorgang (wie das Lesen einer großen Datei oder eine Netzwerkanfrage) ausgeführt wird, wird das gesamte Programm blockiert und kann keine anderen Anfragen bearbeiten.

Asynchrone Programmierung löst dieses Problem:

  • Verbessert den Anwendungsdurchsatz
  • Nutzt Systemressourcen effektiver
  • Bietet eine bessere Benutzererfahrung

Drei Hauptwege der asynchronen Programmierung in Node.js

1. Callback-Funktionen (Callbacks)

Callback-Funktionen sind die grundlegendste Methode zur asynchronen Verarbeitung in Node.js. Sie funktionieren, indem eine Funktion als Argument an eine andere Funktion übergeben wird, und diese Funktion wird aufgerufen, nachdem der Vorgang abgeschlossen ist.

Beispiel

const fs = require('fs');

// Datei asynchron lesen
fs.readFile('example.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Fehler beim Lesen der Datei:', err);
    return;
  }
  console.log('File content:', data);
});

console.log('Anfrage zum Lesen der Datei gesendet, fahre mit der Ausführung von anderem Code fort...');

Funktionen:

  • Einfach und unkompliziert
  • Führt leicht zur "Callback-Hölle"
  • Fehlerbehandlung ist nicht intuitiv genug

2、Promise

Promise ist eine in ES6 eingeführte Lösung für asynchrone Programmierung. Sie steht für den eventuellen Abschluss oder das Scheitern einer asynchronen Operation und ihren resultierenden Wert.

Beispiel

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

// Datei mit Promise lesen
fs.readFile('example.txt', 'utf8')
  .then(data => {
    console.log('File content:', data);
  })
  .catch(err => {
    console.error('Fehler beim Lesen der Datei:', err);
  });

console.log('Anfrage zum Lesen der Datei gesendet, fahre mit der Ausführung von anderem Code fort...');

Funktionen:

  • Verkettete Aufrufe, wodurch die Callback-Hölle vermieden wird
  • Besserer Mechanismus zur Fehlerbehandlung
  • Der Zustand ist unumkehrbar (pending → fulfilled oder rejected)

3、async/await

async/await ist syntaktischer Zucker, der in ES8 eingeführt wurde, auf Promise basiert und asynchronen Code wie synchronen Code aussehen lässt.

Beispiel

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

async function readFile() {
  try {
    const data = await fs.readFile('example.txt', 'utf8');
    console.log('File content:', data);
  } catch (err) {
    console.error('Fehler beim Lesen der Datei:', err);
  }
}

readFile();
console.log('Anfrage zum Lesen der Datei gesendet, fahre mit der Ausführung von anderem Code fort...');

Funktionen:

  • Code ist prägnanter und lesbarer
  • Fehlerbehandlung verwendet try/catch-Struktur
  • Muss in async-Funktionen verwendet werden

Best Practices für Fehlerbehandlung

Fehlerbehandlung in Callback-Funktionen

Beispiel

fs.readFile('example.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Fehler beim Lesen der Datei:', err);
    return; // Wichtig: Nach der Fehlerbehandlung return verwenden, um die Ausführung des nachfolgenden Codes zu vermeiden
  }
  console.log('File content:', data);
});

Promise-Fehlerbehandlung

Beispiel

fs.readFile('example.txt', 'utf8')
  .then(data => {
    console.log('File content:', data);
    return someOtherAsyncOperation(data);
  })
  .then(result => {
    console.log('Ergebnis des zweiten Schritts:', result);
  })
  .catch(err => {
    console.error('Fehler während der Verarbeitung:', err);
  });

Fehlerbehandlung mit async/await

Beispiel

async function processData() {
  try {
    const data = await fs.readFile('example.txt', 'utf8');
    const result = await someOtherAsyncOperation(data);
    console.log('Endergebnis:', result);
  } catch (err) {
    console.error('Fehler während der Verarbeitung:', err);
  }
}

processData();

Leistungsüberlegungen

  1. Blockieren der Event-Loop vermeiden: Lang andauernder synchroner Code blockiert die gesamte Anwendung
  2. Asynchrone Operationen angemessen einsetzen: Nicht alle Operationen müssen asynchron sein; einfache Berechnungsaufgaben können mit synchronen Methoden effizienter sein
  3. Den Grad der Nebenläufigkeit steuern: Zu viele parallele asynchrone Operationen können zu Ressourcenerschöpfung führen
  4. Streams zum Verarbeiten großer Dateien verwenden: Für große Dateien Streams statt einmaligem vollständigem Einlesen verwenden
Andere Erweiterungen