7.4.6 Callbacks

Callbacks verstehen

Achtung: Diese Seite ist nicht trivial. Aber Sie haben hier die einmalige Chance Callbacks wirklich zu verstehen und ich kenne keine andere Seite, die Callbacks so gut beschreibt. Aber wenn Sie eine ebenso gute oder bessere Beschreibung kennen, dann lassen Sie es mich gerne wissen.

In JavaScript ist ein Callback eine Funktion, die als Argument an eine "andere" Funktion übergeben wird. Die "andere" Funktion entscheidet, wann und wie der Callback ausgeführt wird.

Stellen Sie sich vor, Sie bestellen in einem Restaurant Essen. Nachdem Sie Ihre Bestellung (den Callback) abgegeben haben, beginnt der Koch (die aufrufende Funktion) mit der Zubereitung (und die Zubereitung dauert Zeit). Sobald das Essen fertig ist (ein bestimmter Zustand erreicht wird - hier die Zeit verstrichen ist), bringt der Kellner es zu Ihnen – der Callback (die Bestellung) wird aufgerufen!


Einfacher Callback

Schauen wir uns ein Programmierbeispiel an: Eine Funktion soll einen Text auf der Konsole ausgeben, aber die genaue Nachricht wird von einem Callback (der Funktion simpleCallback) bereitgestellt. So sind wir sehr flexibel, welche Nachricht aufgerufen wird.

"use strict";

// Callback-Funktion: Sie gibt eine Nachricht auf der Konsole aus.
const simpleCallback = function() {
  document.write("Deine Bestellung ist fertig!");
};

// Funktion, die den Callback aufruft
const callerFunction = function(callback) {
  document.write("Bestellung wird bearbeitet...");

  // Virtuelle Bearbeitungszeit durch Schleife simulieren
  for (let i = 1; i <= 5; i++) {
    document.write("Bearbeitungszeit: " + i + "Minuten...<br>");
  }

  // Überprüfung: Ist der Parameter eine Funktion?
  if (typeof callback === "function") {
    callback(); // Der Callback wird aufgerufen.
  }
};

// Test: Die Funktion "callerFunction" ruft den Callback auf
callerFunction(simpleCallback);

// Ausgabe:
// Bestellung wird bearbeitet...
// Bearbeitungszeit: 1 Minuten...
// Bearbeitungszeit: 2 Minuten...
// Bearbeitungszeit: 3 Minuten...
// Bearbeitungszeit: 4 Minuten...
// Bearbeitungszeit: 5 Minuten...
// Deine Bestellung ist fertig!

Erklärung

  1. simpleCallback: Dies ist die Callback-Funktion, die eine Nachricht auf der Konsole ausgibt.
  2. callerFunction: Eine Funktion, die etwas tut (z. B. "Bestellung wird bearbeitet...") und dann den Callback aufruft, wenn er als Funktion übergeben wurde.
  3. Warum? So können Sie einer Funktion flexible Aufgaben geben, je nachdem, welcher Callback übergeben wird.

Motivation: Warum Callbacks?

Stellen Sie sich vor, Sie entwickeln ein Spiel. Der Spieler erreicht ein Level, und die Funktion, die Level-ups verarbeitet, ruft eine Callback-Funktion auf, um je nach Level eine Belohnung zu vergeben.

Callbacks sind wichtig, weil sie Flexibilität bieten. Sie lassen Sie entscheiden, was passieren soll, sobald ein bestimmter Zustand erreicht ist.


Übung: Ihr eigenes Callback mit Bearbeitungszeit

Aufgabe: Ergänzen Sie die Funktion restaurantOrder, sodass sie einen Restaurantnamen und einen Callback entgegennimmt. Die Bearbeitungszeit wird durch eine Schleife simuliert: Sushi dauert 5 Minuten, Pizza dauert 8 Minuten. Der Callback soll am Ende der Bearbeitung eine Nachricht anzeigen.

Hinweis: Überprüfen Sie, ob der übergebene Callback tatsächlich eine Funktion ist, bevor Sie ihn aufrufen.

"use strict";
// Funktion mit Callback-Logik
const restaurantOrder = function(restaurantName, callback) {
  document.write("Bestellung beim Restaurant " + restaurantName + " aufgegeben.<br>");



  // Ergänzen Sie hier die Bearbeitungszeit für Sushi (5 Minuten) und Pizza (8 Minuten)



  if (typeof callback === "function") {
    callback();
  }
};

// Callback für SushiBar
const sushiCallback = function() {
  document.write("Dein Sushi ist fertig!<br>");
};

// Callback für PizzaHaus
const pizzaCallback = function() {
  document.write("Deine Pizza ist fertig!<br>");
};


// Hauptprogramm
restaurantOrder("SushiBar", sushiCallback);
restaurantOrder("PizzaHaus", pizzaCallback);

So soll es im Browser aussehen

Lösung
    "use strict";
    // Funktion mit Callback-Logik
    const restaurantOrder = function(restaurantName, callback) {
      document.write("Bestellung beim Restaurant " + restaurantName + " aufgegeben.<br>");

      // Zubereitungszeit abhängig vom Restaurant festlegen
      let minutes;
      if (restaurantName === "SushiBar") {
        minutes = 5;
      } else if (restaurantName === "PizzaHaus") {
        minutes = 8;
      } else {
        minutes = 10; // Standardwert
      }

      // Zubereitungszeit simulieren
      for (let i = 1; i <= minutes; i++) {
        document.write("Minute " + i + "...<br>");
      }

      if (typeof callback === "function") {
        callback();
      }
    };

    // Callback für SushiBar
    const sushiCallback = function() {
      document.write("Dein Sushi ist fertig!<br>");
    };

    // Callback für PizzaHaus
    const pizzaCallback = function() {
      document.write("Deine Pizza ist fertig!<br>");
    };


    // Hauptprogramm
    restaurantOrder("SushiBar", sushiCallback);
    restaurantOrder("PizzaHaus", pizzaCallback);

Anonyme Callbacks

In der Praxis werden Callbacks oft direkt bei der Übergabe als anonyme Funktionen definiert. Anonyme Funktionen sind Funktionen ohne Namen, die direkt an die aufrufende Funktion übergeben werden. Dies macht den Code kompakter und flexibler, da keine separate Funktionsdeklaration nötig ist.

Schauen wir uns das folgende Beispiel an, in dem anonyme Funktionen als Callback verwendet werden:

"use strict";
const restaurantOrder = function(restaurantName, callback) {
  document.write("Bestellung beim Restaurant " + restaurantName + " aufgegeben.<br>");

  let minutes = restaurantName === "SushiBar" ? 5 : 8;
  for (let i = 1; i <= minutes; i++) {
    document.write("Minute " + i + "...<br>");
  }

  if (typeof callback === "function") {
    callback();
  }
};

// Hauptprogramm: Anonyme Callback-Funktion bei der Übergabe
// Die zu übergebene Funktion steht jetzt direkt in der Übergabe
restaurantOrder("SushiBar", function() {
  document.write("Dein Sushi ist fertig!<br>");
});

restaurantOrder("PizzaHaus", function() {
  document.write("Deine Pizza ist fertig!<br>");
});

Erklärung: - Diese Funktionen haben keinen Namen (sind also anonym) und werden direkt als Argumente an restaurantOrder übergeben. - Sie sind anonym, weil sie nicht separat deklariert wurden und keinen Bezeichner besitzen. - Stattdessen werden sie nur an dieser Stelle verwendet, um den spezifischen Text für Sushi und Pizza anzuzeigen.


Callbacks mit Parametern

Callbacks können Parameter empfangen, um spezifische Daten zwischen Funktionen zu übergeben. Dies macht Callbacks äußerst flexibel und ermöglicht es, kontextbezogene Informationen zu verarbeiten.

Warum Parameter in Callbacks?

  1. Datenweitergabe: Die aufrufende Funktion kann Daten an den Callback weiterreichen, die dieser benötigt, um spezifische Aufgaben zu erledigen.
  2. Flexibilität: Mit Parametern können Sie denselben Callback an mehreren Stellen verwenden, aber unterschiedlich anpassen, je nachdem, welche Daten übergeben werden.
  3. Klarheit: Es wird deutlich, welche Informationen zwischen den Funktionen fließen, und der Code bleibt gut lesbar.

Beispiel: Callback mit Parametern

Das folgende Beispiel zeigt, wie eine Funktion namens restaurantOrder Informationen wie den Namen des Restaurants und die Bearbeitungszeit an einen Callback übergibt. Der Callback verarbeitet diese Daten und zeigt sie an.

"use strict";
const restaurantOrder = function(restaurantName, callback) {
  document.write("Bestellung beim Restaurant " 
                + restaurantName + " aufgegeben.<br>");

  // Bearbeitungszeit abhängig vom Restaurant
  let minutes = restaurantName === "SushiBar" ? 5 : 8;

  // Bearbeitungszeit simulieren
  for (let i = 1; i <= minutes; i++) {
    document.write("Minute " + i + "...<br>");
  }

  // Callback ausführen und Parameter übergeben
  if (typeof callback === "function") {
    callback(restaurantName, minutes);
  }
};

// Der Callback verarbeitet die übergebenen Parameter
const detailedCallback = function(name, time) {
  document.write(name + " hat " + time + " Minuten gebraucht.<br>");
};

// Hauptprogramm
restaurantOrder("SushiBar", detailedCallback);
restaurantOrder("PizzaHaus", detailedCallback);

Erklärung

  1. restaurantOrder Funktion

    • Diese Funktion simuliert eine Bestellung.
    • Sie berechnet die Bearbeitungszeit basierend auf dem Restaurantnamen.
    • Nach der Bearbeitung ruft sie den übergebenen Callback auf und übergibt den Restaurantnamen sowie die Bearbeitungszeit.
  2. detailedCallback Funktion

    • Dies ist der Callback, der zwei Parameter (name und time) empfängt.
    • Der Callback verwendet die übergebenen Daten, um eine spezifische Nachricht anzuzeigen.
    • Da ein Name (detailedCallback) angegeben ist, ist es kein anonymer Callback
  3. Hauptprogramm

    • Der Callback wird an restaurantOrder übergeben.
    • Die Funktion wird für unterschiedliche Restaurants (SushiBar, PizzaHaus) aufgerufen, wodurch der Callback je nach Restaurantnamen und Bearbeitungszeit unterschiedliche Nachrichten anzeigt.

So sieht es im Browser aussehen


Eigener Callback mit Parametern

Aufgabe: Schreiben Sie einen eigenen Callback, der für jedes Restaurant eine zusätzliche Nachricht anzeigt, z. B.:

  • Für die "SushiBar": "Sushi ist schnell fertig!"
  • Für das "PizzaHaus": "Pizza braucht etwas länger, aber es lohnt sich!"

Hinweis: Verwenden Sie die Parameter, um den Namen des Restaurants zu überprüfen und eine passende Nachricht anzuzeigen.

Lösung
const detailedCallbackWithMessage = function(name, time) {
  document.write(name + " hat " + time + " Minuten gebraucht.<br>");
  if (name === "SushiBar") {
    document.write("Sushi ist schnell fertig!<br>");
  } else if (name === "PizzaHaus") {
    document.write("Pizza braucht etwas länger, aber es lohnt sich!<br>");
  } else {
    document.write("Vielen Dank für Ihre Geduld!<br>");
  }
};

restaurantOrder("SushiBar", detailedCallbackWithMessage);
restaurantOrder("PizzaHaus", detailedCallbackWithMessage);

forEach() mit Callback

Die Array-Methode forEach() ist eine häufig genutzte Funktion in JavaScript, um auf jedes Element eines Arrays eine spezifische Funktion anzuwenden. Dabei ist die Callback-Funktion flexibel, sodass Sie selbst definieren können, was mit den Elementen des Arrays passieren soll.

1
2
3
4
5
6
7
8
9
"use strict";

// Array mit Studierenden
const students = ["Lena", "Tobias", "Miriam"];

// Funktion, die auf jedes Array-Element angewendet wird
students.forEach(function(student, index) {
  document.write("Student " + (index + 1) + ": " + student + "<br>");
});

Erklärung

  1. Array students:
    Das Array enthält die Namen der Studierenden, auf die wir eine Operation anwenden möchten. Es ist die Datenquelle, auf der die Methode forEach() arbeitet.

  2. Callback-Funktion:
    Die Callback-Funktion ist der zentrale Bestandteil von forEach(). Sie wird für jedes Element des Arrays ausgeführt und übernimmt die Logik, die auf das jeweilige Element angewendet werden soll.

  3. Wo ist die Callback-Funktion?
    Im Beispiel ist die Callback-Funktion in der Methode forEach() eingebettet. Sie wird als anonyme Funktion direkt übergeben:
    1
    2
    3
    function(student, index) {
      document.write("Student " + (index + 1) + ": " + student + "<br>");
    }
    
  4. Parameter der Callback-Funktion:
    Die Callback-Funktion hat Zugriff auf:

    • student: Das aktuelle Element des Arrays (z. B. "Lena", "Tobias", "Miriam").
    • index: Die Position des aktuellen Elements im Array (z. B. 0, 1, 2).
  5. Output:
    Für jedes Element des Arrays wird die Callback-Funktion ausgeführt. Die Funktion generiert eine Zeile mit:

    • Der Position des Studierenden im Array (beginnend bei 1).
    • Dem Namen des Studierenden.
      Diese Zeilen werden mithilfe von document.write() ausgegeben.

Zusammenfassung:
Die Callback-Funktion ist die anonyme Funktion, die an forEach() übergeben wird. Sie führt die individuelle Verarbeitung jedes Array-Elements durch.


Erweiterung: Komplexere Operationen

Statt einer einfachen Nachricht können wir auch komplexere Operationen durchführen, z. B. die Länge jedes Namens berechnen:

"use strict";

// Array mit Studierenden
const students = ["Lena", "Tobias", "Miriam"];

// Längere Funktion mit Berechnung
students.forEach(function(student, index) {
  const length = student.length;
  document.write("Student " + (index + 1) + ": " + student + " (Name hat " + length + " Zeichen)<br>");
});

Übung zur Erweiterung des Beispiels

Ergänzen Sie das obige Beispiel so, dass für Studierende mit Namen, die länger als 5 Zeichen sind, zusätzlich die Nachricht "Ihr Name ist relativ lang" ausgegeben wird.

Lösung
"use strict";

// Array mit Studierenden
const students = ["Lena", "Tobias", "Miriam"];

// Erweiterte Funktion mit Bedingung
students.forEach(function(student, index) {
  const length = student.length;

  document.write("Student " + (index + 1) + ": " + student 
              + " (Name hat " + length + " Zeichen)<br>");

  if (length > 5) {
    document.write("  Hinweis: Der Name von " + student + " ist relativ lang.<br>");
  }
});

Übung: Begrüßung mit einem Callback

Ergänzen Sie die folgende Funktion so, dass sie den Benutzer nach seinem Namen fragt und eine personalisierte Begrüßung ausgibt.

1
2
3
4
5
6
7
"use strict";
const exerciseFunction = function(callback) {
  const name = prompt("Bitte geben Sie Ihren Namen ein:");
  callback(name);
};

// Definieren Sie den Callback hier...
Lösung
"use strict";
const exerciseFunction = function(callback) {
  const name = prompt("Bitte geben Sie Ihren Namen ein:");
  callback(name);
};

// Begrüßungsfunktion
const greetingFunction = function(name) {
  document.write("Hallo " + name + "!<br>");
};

// Aufruf mit Callback
exerciseFunction(greetingFunction);

// Alternativ mit anonymer Callback-Funktion
exerciseFunction(function(name) {
  document.write("Willkommen, " + name + "!<br>");
});

Fazit

Callbacks sind in JavaScript ein essentielles Konzept. Sie ermöglichen die Organisation von Abläufen und die dynamische Anpassung von Funktionen. Diese Technik bildet die Grundlage für fortgeschrittene Themen wie Promises und async/await, die in diesem Script jedoch nicht behandelt werden.

Weiterführende Links