Suche…


Beispiele für einfache Rückrufnutzung

Callbacks bieten eine Möglichkeit, die Funktionalität einer Funktion (oder Methode) zu erweitern, ohne den Code zu ändern . Dieser Ansatz wird häufig in Modulen (Bibliotheken / Plugins) verwendet, deren Code nicht geändert werden soll.

Angenommen, wir haben die folgende Funktion geschrieben und berechnen die Summe eines gegebenen Wertebereichs:

function foo(array) {
    var sum = 0;
    for (var i = 0; i < array.length; i++) {
        sum += array[i];
    }
    return sum;
}

Angenommen, wir möchten mit jedem Wert des Arrays etwas tun, z. B. mit alert() anzeigen. Wir könnten die entsprechenden Änderungen im Code von foo vornehmen:

function foo(array) {
    var sum = 0;
    for (var i = 0; i < array.length; i++) {
        alert(array[i]);
        sum += array[i];
    }
    return sum;
}

Was aber, wenn wir uns dazu entscheiden, console.log anstelle von alert() ? Natürlich ist es keine gute Idee, den Code von foo ändern, wenn wir uns dazu entscheiden, mit jedem Wert etwas anderes zu tun. Es ist viel besser, die Möglichkeit zu haben, unsere Meinung zu ändern, ohne den Code von foo zu ändern. Das ist genau der Anwendungsfall für Rückrufe. Wir müssen nur die Unterschrift und den Körper von foo leicht ändern:

function foo(array, callback) {
    var sum = 0;
    for (var i = 0; i < array.length; i++) {
        callback(array[i]);
        sum += array[i];
    }
    return sum;
}

Jetzt können wir das Verhalten von foo ändern, indem wir nur die Parameter ändern:

var array = [];
foo(array, alert);
foo(array, function (x) {
    console.log(x);
});

Beispiele mit asynchronen Funktionen

In jQuery ist die $.getJSON() -Methode zum Abrufen von JSON-Daten asynchron. Das Übergeben von Code in einem Rückruf stellt daher sicher, dass der Code aufgerufen wird, nachdem der JSON abgerufen wurde.

$.getJSON() Syntax:

$.getJSON( url, dataObject, successCallback );

Beispiel für $.getJSON() Code:

$.getJSON("foo.json", {}, function(data) {
    // data handling code
});

Folgendes würde nicht funktionieren, da der Code für die Datenverarbeitung wahrscheinlich aufgerufen wird, bevor die Daten tatsächlich empfangen werden, da die $.getJSON Funktion eine unbestimmte Zeit in $.getJSON nimmt und den Aufrufstack nicht $.getJSON , während sie auf den JSON wartet.

$.getJSON("foo.json", {});
// data handling code

Ein anderes Beispiel für eine asynchrone Funktion ist die animate() Funktion von jQuery. Da das Ausführen der Animation eine bestimmte Zeit in Anspruch nimmt, ist es manchmal wünschenswert, Code unmittelbar nach der Animation auszuführen.

.animate() Syntax:

jQueryElement.animate( properties, duration, callback );

Um beispielsweise eine Ausblendanimation zu erstellen, nach der das Element vollständig ausgeblendet ist, kann der folgende Code ausgeführt werden. Beachten Sie die Verwendung des Rückrufs.

elem.animate( { opacity: 0 }, 5000, function() {
    elem.hide();
} );

Dadurch kann das Element direkt nach dem Ausführen der Funktion ausgeblendet werden. Dies unterscheidet sich von:

elem.animate( { opacity: 0 }, 5000 );
elem.hide();

weil letztere nicht auf den Abschluss von animate() (eine asynchrone Funktion) wartet und das Element daher sofort ausgeblendet wird, was zu einem unerwünschten Effekt führt.

Was ist ein Rückruf?

Dies ist ein normaler Funktionsaufruf:

console.log("Hello World!");

Wenn Sie eine normale Funktion aufrufen, führt sie ihre Aufgabe aus und gibt die Kontrolle an den Anrufer zurück.

Manchmal muss eine Funktion jedoch die Kontrolle an den Anrufer zurückgeben, um seine Arbeit zu erledigen:

[1,2,3].map(function double(x) {
    return 2 * x;
});

Im obigen Beispiel wird die Funktion double ist ein Rückruf für die Funktion der map , weil:

  1. Die Funktion double wird auf die Funktion gegeben map vom Anrufer.
  2. Die Funktion der map muss die Funktion aufrufen double null oder mehr Male , um seine Arbeit zu tun.

Somit ist die Funktion der map ist im Wesentlichen die Steuerung zurück an den Anrufer zurück jedes Mal , es die Funktion aufruft double . Daher der Name "Rückruf".


Funktionen können mehr als einen Rückruf annehmen:

promise.then(function onFulfilled(value) {
    console.log("Fulfilled with value " + value);
}, function onRejected(reason) {
    console.log("Rejected with reason " + reason);
});

Hier akzeptiert die Funktion then zwei Callback-Funktionen, onFulfilled und onRejected . Außerdem wird tatsächlich nur eine dieser beiden Rückruffunktionen aufgerufen.

Interessanter ist, dass die Funktion then zurückkehrt, bevor einer der Callbacks aufgerufen wird. Daher kann eine Rückruffunktion auch aufgerufen werden, nachdem die ursprüngliche Funktion zurückgekehrt ist.

Fortsetzung (synchron und asynchron)

Callbacks können verwendet werden, um Code bereitzustellen, der nach Abschluss einer Methode ausgeführt werden soll:

/**
 * @arg {Function} then continuation callback
 */
function doSomething(then) {
  console.log('Doing something');
  then();
}

// Do something, then execute callback to log 'done'
doSomething(function () {
  console.log('Done');
});

console.log('Doing something else');

// Outputs:
//   "Doing something"
//   "Done"
//   "Doing something else"

Die obige Methode doSomething() wird synchron mit den Callback-Ausführungsblöcken ausgeführt, bis doSomething() zurückkehrt, um sicherzustellen, dass der Callback ausgeführt wird, bevor der Interpreter weitergeht.

Rückrufe können auch verwendet werden, um Code asynchron auszuführen:

doSomethingAsync(then) {
  setTimeout(then, 1000);
  console.log('Doing something asynchronously');
}

doSomethingAsync(function() {
  console.log('Done');
});

console.log('Doing something else');

// Outputs:
//   "Doing something asynchronously"
//   "Doing something else"
//   "Done"

Die then Rückrufe sind als Fortsetzungen der doSomething() Methoden. Das Bereitstellen eines Rückrufs als letzte Anweisung in einer Funktion wird als Tail-Call bezeichnet , der von den ES2015-Interpreters optimiert wird .

Fehlerbehandlung und Kontrollflussverzweigung

Callbacks werden häufig zur Fehlerbehandlung verwendet. Dies ist eine Form der Steuerungsflussverzweigung, bei der einige Anweisungen nur ausgeführt werden, wenn ein Fehler auftritt:

const expected = true;

function compare(actual, success, failure) {
  if (actual === expected) {
    success();
  } else {
    failure();
  }
}

function onSuccess() {
  console.log('Value was expected');
}

function onFailure() {
  console.log('Value was unexpected/exceptional');
}

compare(true, onSuccess, onFailure);
compare(false, onSuccess, onFailure);

// Outputs:
//   "Value was expected"
//   "Value was unexpected/exceptional"

Die Codeausführung in compare() oben hat zwei mögliche Verzweigungen: success wenn die erwarteten und tatsächlichen Werte gleich sind, und error wenn sie unterschiedlich sind. Dies ist besonders nützlich, wenn der Steuerfluss nach einer asynchronen Anweisung verzweigen soll:

function compareAsync(actual, success, failure) {
  setTimeout(function () {
    compare(actual, success, failure)
  }, 1000);
}

compareAsync(true, onSuccess, onFailure);
compareAsync(false, onSuccess, onFailure);
console.log('Doing something else');

// Outputs:
//   "Doing something else"
//   "Value was expected"
//   "Value was unexpected/exceptional"

Es sollte beachtet werden, dass sich mehrere Callbacks nicht gegenseitig ausschließen müssen - beide Methoden können aufgerufen werden. In ähnlicher Weise könnte das compare() mit optionalen Rückrufen geschrieben werden (unter Verwendung eines Noop als Standardwert - siehe Null-Objekt-Muster ).

Rückrufe und `this`

Wenn Sie einen Rückruf verwenden, möchten Sie häufig auf einen bestimmten Kontext zugreifen.

function SomeClass(msg, elem) {
  this.msg = msg;
  elem.addEventListener('click', function() {
    console.log(this.msg);  // <= will fail because "this" is undefined
  });
}

var s = new SomeClass("hello", someElement);

Lösungen

  • Verwenden Sie bind

    bind erzeugt effektiv eine neue Funktion, die this auf alles setzt, was zum bind und ruft dann die ursprüngliche Funktion auf.

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', function() {
          console.log(this.msg);  
        }.bind(this));  // <=-  bind the function to `this`
      }
    
  • Verwenden Sie Pfeilfunktionen

    Pfeil Funktionen automatisch binden die aktuellen this Kontext.

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click',() => {   // <=-  arrow function binds `this`
          console.log(this.msg);  
        });
      }
    

Oft möchten Sie eine Member-Funktion aufrufen, im Idealfall übergeben Sie alle an das Ereignis übergebenen Argumente an die Funktion.

Lösungen:

  • Verwenden Sie bind

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', this.handleClick.bind(this));
      }
    
      SomeClass.prototype.handleClick = function(event) {
        console.log(event.type, this.msg);
      };
    
  • Verwenden Sie die Pfeilfunktionen und den Restoperator

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', (...a) => this.handleClick(...a));
      }
    
      SomeClass.prototype.handleClick = function(event) {
        console.log(event.type, this.msg);
      };
    
  • Insbesondere für DOM-Ereignis-Listener können Sie die EventListener Schnittstelle implementieren

      function SomeClass(msg, elem) {
        this.msg = msg;
        elem.addEventListener('click', this);
      }
    
      SomeClass.prototype.handleEvent = function(event) {
        var fn = this[event.type];
        if (fn) {
           fn.apply(this, arguments);
        }
      };
      
      SomeClass.prototype.click = function(event) {
        console.log(this.msg);
      };
    

Rückruf über Pfeilfunktion

Die Verwendung der Pfeilfunktion als Rückruffunktion kann Codezeilen reduzieren.

Die Standardsyntax für die Pfeilfunktion lautet

() => {}

Dies kann als Rückruf verwendet werden

Zum Beispiel, wenn wir alle Elemente in einem Array drucken möchten [1,2,3,4,5]

Ohne Pfeilfunktion sieht der Code so aus

[1,2,3,4,5].forEach(function(x){
                 console.log(x);
            }

Mit der Pfeilfunktion kann es auf reduziert werden

[1,2,3,4,5].forEach(x => console.log(x));

Hier ist die Callback-Funktion function(x){console.log(x)} auf x=>console.log(x) reduziert.



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