Wie erklärt man Rückrufe in einfachem Englisch? Wie unterscheiden sie sich vom Aufrufen einer Funktion von einer anderen Funktion?

341

Wie erklärt man Rückrufe im Klartext? Wie unterscheiden sie sich vom Aufrufen einer Funktion von einer anderen Funktion, wobei ein Kontext von der aufrufenden Funktion übernommen wird? Wie kann ihre Macht einem unerfahrenen Programmierer erklärt werden?

Yahoo-Me
quelle
1
Ich glaube, der wissenschaftliche Name dafür ist Continuation-Passing-Stil. Sie können dies im Wiki suchen.
Ming_Codes
1
Es gibt ein paar gute Antworten auf eine identische Frage auf Quoren auch
dbr
Die beste Erklärung für einen Rückruf, die ich jemals gefunden habe youtube.com/watch?v=xHneyv38Jro
Sameer Sinha

Antworten:

114

Oft muss eine Anwendung je nach Kontext / Status unterschiedliche Funktionen ausführen. Dazu verwenden wir eine Variable, in der wir die Informationen über die aufzurufende Funktion speichern. Je nach Bedarf setzt die Anwendung diese Variable mit den Informationen über die aufzurufende Funktion und ruft die Funktion mit derselben Variablen auf.

In Javascript ist das Beispiel unten. Hier verwenden wir das Methodenargument als Variable, in der wir Informationen über die Funktion speichern.

function processArray(arr, callback) {
    var resultArr = new Array(); 
    for (var i = arr.length-1; i >= 0; i--)
        resultArr[i] = callback(arr[i]);
    return resultArr;
}

var arr = [1, 2, 3, 4];
var arrReturned = processArray(arr, function(arg) {return arg * -1;});
// arrReturned would be [-1, -2, -3, -4]
Niraj Nawanit
quelle
18
Während dies technisch gesehen ein Rückruf ist, klingt die gegebene Erklärung von einem allgemeinen Funktionszeiger undeutlich. Es wäre hilfreich, einige Gründe dafür anzugeben, warum ein Rückruf verwendet werden könnte.
Eric
4
Ich verstehe diese Antwort nicht. Kann es mehr um Erklärungen als um den Code gehen?
Abhishek Singh
Warum können wir nicht das tun, was wir in der aufgerufenen Funktion (the function(arg)) in der processArray(arr,callback)Funktion tun
Abhi
1
@JoSmo du bist teilweise richtig. Übergeben Sie eine Variable + Rückruffunktion als Parameter, indem Sie das mit der Variablen von der ursprünglichen Funktion erstellte Ergebnis übernehmen und zur weiteren Verarbeitung in der Rückruffunktion übergeben. Beispiel: func1 (a, callback_func) {v = a + 1} und es gibt eine vordefinierte Rückruffunktion: callback_func (v) {return v + 1;} Dies erhöht a um 2, wenn Sie also das Argument der Ganzzahl 4 in übergeben "a" -Parameter, der callback_funct gibt als Ergebnis 6 zurück. Lesen Sie diese beste Quelle von callbackhell.com, die ich gefunden habe.
Dung
Diese Antwort ist nicht klar. Könnte einfacher und klarer sein!
Arjun Kalidas
545

Ich werde versuchen, das absolut einfach zu halten. Ein "Rückruf" ist eine Funktion, die von einer anderen Funktion aufgerufen wird, die die erste Funktion als Parameter verwendet. Ein "Rückruf" ist oft eine Funktion, die aufgerufen wird, wenn etwas passiert. Dass etwas in Programmierersprache als "Ereignis" bezeichnet werden kann.

Stellen Sie sich dieses Szenario vor: Sie erwarten ein Paket in ein paar Tagen. Das Paket ist ein Geschenk für Ihren Nachbarn. Sobald Sie das Paket erhalten haben, möchten Sie es daher zu den Nachbarn bringen. Sie sind nicht in der Stadt und hinterlassen Anweisungen für Ihren Ehepartner.

Sie könnten ihnen sagen, sie sollen das Paket holen und es zu den Nachbarn bringen. Wenn Ihr Ehepartner so dumm wie ein Computer wäre, würden sie an der Tür sitzen und auf das Paket warten, bis es kommt (sonst nichts tun), und wenn es dann kommt, würden sie es zu den Nachbarn bringen. Aber es gibt einen besseren Weg. Sagen Sie Ihrem Ehepartner, dass er das Paket EINMAL über die Nachbarn bringen soll, wenn er es erhält. Dann können sie normal leben, bis sie das Paket erhalten.

In unserem Beispiel ist der Empfang des Pakets das "Ereignis" und das Bringen zu den Nachbarn der "Rückruf". Ihr Ehepartner "führt" Ihre Anweisungen aus, um das Paket erst dann zu bringen, wenn das Paket eintrifft. Viel besser!

Diese Art des Denkens ist im täglichen Leben offensichtlich, aber Computer haben nicht den gleichen gesunden Menschenverstand. Überlegen Sie, wie Programmierer normalerweise in eine Datei schreiben:

fileObject = open(file)
# now that we have WAITED for the file to open, we can write to it
fileObject.write("We are writing to the file.")
# now we can continue doing the other, totally unrelated things our program does

Hier warten wir darauf, dass die Datei geöffnet wird, bevor wir darauf schreiben. Dies "blockiert" den Ausführungsfluss und unser Programm kann keine der anderen Dinge tun, die es möglicherweise tun muss! Was wäre, wenn wir dies stattdessen tun könnten:

# we pass writeToFile (A CALLBACK FUNCTION!) to the open function
fileObject = open(file, writeToFile)
# execution continues flowing -- we don't wait for the file to be opened
# ONCE the file is opened we write to it, but while we wait WE CAN DO OTHER THINGS!

Es stellt sich heraus, dass wir dies mit einigen Sprachen und Frameworks tun. Es ist ziemlich cool! Schauen Sie sich Node.js an, um sich mit dieser Art des Denkens vertraut zu machen.

Josh Imhoff
quelle
6
Dies ist korrekt, deckt jedoch nicht alle gängigen Anwendungsfälle für Rückrufe ab. Oft verwenden Sie Rückrufe, wenn Sie eine Funktion mit Argumenten aufrufen müssen, die im Prozess einer anderen Funktion verarbeitet werden. Zum Beispiel nehmen in PHP array_filter () und array_map () Rückrufe entgegen, die in einer Schleife aufgerufen werden sollen.
Haralan Dobrev
2
Ist das Beispiel für eine Schreibdatei angemessen? Es scheint Annahmen darüber zu treffen, wie es openfunktioniert. Es ist plausibel, dass openintern blockiert werden kann, während darauf gewartet wird, dass das Betriebssystem seine schwarze Magie ausführt, auf der der Rückruf ausgeführt wird. In einem solchen Fall gibt es keinen Unterschied im Ergebnis.
Kenneth K.
23
Schöne Erklärung, aber ich bin ein bisschen verwirrend. Ist Callback Multithreaded?
Premraj
1
Tolles Beispiel! War auf der Suche nach einem einfachen Englisch überall und dies ist das erste, das ich bisher gefunden habe :)
ChristoKiwi
1
Was macht die offene Funktion in Ihrem Beispiel nicht blockierend? offen kann noch den Fluss der Ausführung blockieren ..
Koray Tugay
82

Wie erklärt man Rückrufe im Klartext?

Im Klartext ist eine Rückruffunktion wie ein Mitarbeiter, der seinen Manager "zurückruft", wenn er eine Aufgabe erledigt hat .

Wie unterscheiden sie sich vom Aufrufen einer Funktion von einer anderen Funktion, wobei ein Kontext von der aufrufenden Funktion übernommen wird?

Es ist wahr, dass Sie eine Funktion von einer anderen Funktion aus aufrufen, aber der Schlüssel ist, dass der Rückruf wie ein Objekt behandelt wird, sodass Sie die aufzurufende Funktion basierend auf dem Status des Systems ändern können (wie das Strategieentwurfsmuster).

Wie kann ihre Macht einem unerfahrenen Programmierer erklärt werden?

Die Leistungsfähigkeit von Rückrufen lässt sich leicht auf Websites im AJAX-Stil erkennen, die Daten von einem Server abrufen müssen. Das Herunterladen der neuen Daten kann einige Zeit dauern. Ohne Rückrufe würde Ihre gesamte Benutzeroberfläche beim Herunterladen der neuen Daten "einfrieren", oder Sie müssten die gesamte Seite aktualisieren und nicht nur einen Teil davon. Mit einem Rückruf können Sie ein Bild "Jetzt laden" einfügen und es nach dem Laden durch die neuen Daten ersetzen.

Code ohne Rückruf:

function grabAndFreeze() {
    showNowLoading(true);
    var jsondata = getData('http://yourserver.com/data/messages.json');
    /* User Interface 'freezes' while getting data */
    processData(jsondata);
    showNowLoading(false);
    do_other_stuff(); // not called until data fully downloaded
}

function processData(jsondata) { // do something with the data
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

Mit Rückruf:

Hier ist ein Beispiel mit einem Rückruf unter Verwendung von getJSON von jQuery :

function processDataCB(jsondata) { // callback: update UI with results
   showNowLoading(false);
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

function grabAndGo() { // and don't freeze
    showNowLoading(true);
    $('#results_messages').html(now_loading_image);
    $.getJSON("http://yourserver.com/data/messages.json", processDataCB);
    /* Call processDataCB when data is downloaded, no frozen User Interface! */
    do_other_stuff(); // called immediately
}

Mit Verschluss:

Oft muss der Rückruf über statedie aufrufende Funktion mit a zugreifen closure, ähnlich wie der Mitarbeiter Informationen vom Manager abrufen muss, bevor er seine Aufgabe ausführen kann . Um das zu erstellen closure, können Sie die Funktion so einbinden, dass die Daten im aufrufenden Kontext angezeigt werden:

/* Grab messages, chat users, etc by changing dtable. Run callback cb when done.*/
function grab(dtable, cb) { 
    if (null == dtable) { dtable = "messages"; }
    var uiElem = "_" + dtable;
    showNowLoading(true, dtable);
    $('#results' + uiElem).html(now_loading_image);
    $.getJSON("http://yourserver.com/user/"+dtable+".json", cb || function (jsondata) {
       // Using a closure: can "see" dtable argument and uiElem variables above.
       var count = jsondata.results ? jsondata.results.length : 0, 
           counterMsg = ['Fetched', count, 'new', dtable].join(' '),
           // no new chatters/messages/etc
           defaultResultsMsg = ['(no new ', dtable, ')'].join(''); 
       showNowLoading(false, dtable);
       $('#counter' + uiElem).text(counterMsg);
       $('#results'+ uiElem).html(jsondata.results || defaultResultsMsg);
    });
    /* User Interface calls cb when data is downloaded */

    do_other_stuff(); // called immediately
}

Verwendungszweck:

// update results_chatters when chatters.json data is downloaded:
grab("chatters"); 
// update results_messages when messages.json data is downloaded
grab("messages"); 
// call myCallback(jsondata) when "history.json" data is loaded:
grab("history", myCallback); 

Schließung

Schließlich ist hier eine Definition closurevon Douglas Crockford :

Funktionen können innerhalb anderer Funktionen definiert werden. Die innere Funktion hat Zugriff auf die Variablen und Parameter der äußeren Funktion. Wenn ein Verweis auf eine innere Funktion erhalten bleibt (z. B. als Rückruffunktion), überleben auch die Vars der äußeren Funktion.

Siehe auch:

user508994
quelle
12
+1. Der erste Absatz geht auf das Geld . Der Rest geht jedoch ziemlich schnell in die Fachsprache der Informatik über.
TarkaDaal
41

Ich bin fassungslos zu sehen, dass so viele intelligente Menschen die Realität nicht betonen, dass das Wort "Rückruf" auf zwei inkonsistente Arten verwendet wird.

In beiden Fällen muss eine Funktion angepasst werden, indem zusätzliche Funktionen (eine anonyme oder benannte Funktionsdefinition) an eine vorhandene Funktion übergeben werden. dh.

customizableFunc(customFunctionality)

Wenn die benutzerdefinierte Funktionalität einfach in den Codeblock eingefügt wird, haben Sie die Funktion wie folgt angepasst.

    customizableFucn(customFunctionality) {
      var data = doSomthing();
      customFunctionality(data);
      ...
    }

Obwohl diese Art der injizierten Funktionalität oft als "Rückruf" bezeichnet wird, gibt es nichts Kontingentes daran. Ein sehr offensichtliches Beispiel ist die forEach-Methode, bei der eine benutzerdefinierte Funktion als Argument angegeben wird, das auf jedes Element in einem Array angewendet wird, um das Array zu ändern.

Dies unterscheidet sich jedoch grundlegend von der Verwendung von "Rückruffunktionen" für die asynchrone Programmierung wie in AJAX oder node.js oder einfach durch die Zuweisung von Funktionen zu Benutzerinteraktionsereignissen (wie Mausklicks). In diesem Fall besteht die gesamte Idee darin, auf das Eintreten eines möglichen Ereignisses zu warten, bevor die benutzerdefinierte Funktionalität ausgeführt wird. Dies ist bei Benutzerinteraktionen offensichtlich, aber auch bei E / A-Prozessen (Eingabe / Ausgabe) wichtig, die einige Zeit in Anspruch nehmen können, z. B. beim Lesen von Dateien von der Festplatte. Hier macht der Begriff "Rückruf" den offensichtlichsten Sinn. Sobald ein E / A-Prozess gestartet wurde (z. B. das Lesen einer Datei von der Festplatte oder eines Servers, um Daten von einer http-Anforderung zurückzugeben), erfolgt eine asynchroneDas Programm wartet nicht darauf, dass es beendet wird. Es kann mit den als nächstes geplanten Aufgaben fortfahren und erst dann mit der benutzerdefinierten Funktionalität antworten, wenn benachrichtigt wurde, dass die Lesedatei oder die http-Anforderung abgeschlossen ist (oder fehlgeschlagen ist) und die Daten für die benutzerdefinierte Funktionalität verfügbar sind. Es ist, als würde man ein Unternehmen am Telefon anrufen und seine "Rückruf" -Nummer hinterlassen, damit es Sie anrufen kann, wenn jemand verfügbar ist, um sich bei Ihnen zu melden. Das ist besser, als lange an der Leitung zu bleiben und sich nicht um andere Angelegenheiten kümmern zu können.

Die asynchrone Verwendung umfasst von Natur aus einige Mittel zum Abhören des gewünschten Ereignisses (z. B. den Abschluss des E / A-Prozesses), sodass die benutzerdefinierte "Rückruf" -Funktionalität ausgeführt wird, wenn sie auftritt (und nur wenn sie auftritt). Im offensichtlichen AJAX-Beispiel wird, wenn die Daten tatsächlich vom Server ankommen, die Rückruffunktion ausgelöst, um diese Daten zum Ändern des DOM zu verwenden und daher das Browserfenster in diesem Umfang neu zu zeichnen.

Um es noch einmal zusammenzufassen. Einige Leute verwenden das Wort "Rückruf", um sich auf jede Art von benutzerdefinierter Funktionalität zu beziehen, die als Argument in eine vorhandene Funktion eingefügt werden kann. Aber zumindest für mich ist die am besten geeignete Verwendung des Wortes, wenn die injizierte "Rückruffunktion" asynchron verwendet wird - nur ausgeführt, wenn ein Ereignis eintritt, über das es darauf wartet, benachrichtigt zu werden.

Robert Polevoi
quelle
Wohin kehrt der Prozess zurück, wenn die Funktion zurückruft? Zum Beispiel, wenn es vier Codezeilen gibt; 1.fileObject = open (Datei, writeToFile); 2. doSomething1 (); 3. doSomething2 (); 4. doSomething3 ().
MagicLAMP
Zeile 1 wird ausgeführt, aber anstatt auf das Öffnen der Datei zu warten, wird mit Zeile 2 und dann mit 3 fortgefahren. An diesem Punkt wird die Datei geöffnet und (sobald Zeile 3 eine Semaphoroperation beendet hat) wird der Programmzähler zurückgerufen, um zu sagen: " Übergeben Sie die Steuerung an writeToFile ", das die Dinge erledigt, und wenn es fertig ist, übergibt es die Steuerung an den Punkt, an dem die INT in Zeile 3 aufgetreten ist, oder an Zeile 4, wenn Zeile 3 beendet werden muss.
MagicLAMP
1
Dies ist eine sehr präzise Erklärung für einen weiteren wichtigen Punkt: z. B. den Unterschied zwischen der Funktion, die als Argument übergeben wurde, Array.prototype.forEach()und der Funktion, die als Argument übergeben wurde setTimeout(), und sie sind Pferde unterschiedlicher Farben, soweit Sie über Ihr Programm nachdenken .
Mikermcneil
26

Für Nicht-Programmierer ist ein Rückruf ein Ausfüllen der Lücke in einem Programm.

Ein häufiger Punkt auf vielen Papierformularen ist "Person, die im Notfall angerufen werden muss". Dort ist eine leere Zeile. Sie schreiben den Namen und die Telefonnummer einer Person ein. Wenn ein Notfall eintritt, wird diese Person angerufen.

  • Jeder bekommt das gleiche leere Formular, aber
  • Jeder kann eine andere Notfall-Kontaktnummer schreiben.

Das ist der Schlüssel. Sie ändern das Formular nicht (den Code, normalerweise den eines anderen). Sie können jedoch fehlende Informationen ( Ihre Nummer) eingeben .

Beispiel 1:

Rückrufe werden als angepasste Methoden verwendet, möglicherweise zum Hinzufügen / Ändern des Verhaltens eines Programms. Nehmen Sie zum Beispiel einen C-Code, der eine Funktion ausführt, aber nicht weiß, wie die Ausgabe gedruckt werden soll. Alles was es tun kann, ist eine Zeichenfolge zu erstellen. Wenn versucht wird, herauszufinden, was mit der Zeichenfolge zu tun ist, wird eine leere Zeile angezeigt. Aber der Programmierer gab Ihnen die Lücke, in die Sie Ihren Rückruf schreiben konnten!

In diesem Beispiel verwenden Sie keinen Bleistift, um eine Lücke auf einem Blatt Papier auszufüllen. Sie verwenden die Funktion set_print_callback(the_callback).

  • Die leere Variable im Modul / Code ist die leere Zeile.
  • set_print_callback ist der Bleistift,
  • und the_callbacksind Ihre Informationen, die Sie ausfüllen.

Sie haben jetzt diese leere Zeile im Programm ausgefüllt. Wann immer die Ausgabe gedruckt werden muss, wird diese leere Zeile angezeigt und die dortigen Anweisungen befolgt (dh die dort eingegebene Funktion aufgerufen). Praktisch ermöglicht dies das Drucken auf dem Bildschirm, in einer Protokolldatei, auf einem Drucker. über eine Netzwerkverbindung oder eine beliebige Kombination davon. Sie haben die Lücke mit dem ausgefüllt, was Sie tun möchten.

Beispiel 2:

Wenn Sie erfahren, dass Sie eine Notrufnummer anrufen müssen, lesen Sie, was auf dem Papierformular steht, und rufen dann die gelesene Nummer an. Wenn diese Zeile leer ist, wird nichts unternommen.

Die GUI-Programmierung funktioniert ähnlich. Wenn Sie auf eine Schaltfläche klicken, muss das Programm herausfinden, was als Nächstes zu tun ist. Es geht und sucht nach dem Rückruf. Dieser Rückruf befindet sich in einem leeren Feld mit der Bezeichnung "Folgendes tun Sie, wenn Sie auf Button1 klicken".

Die meisten IDEs füllen die Lücke automatisch für Sie aus (schreiben Sie die grundlegende Methode), wenn Sie dazu aufgefordert werden (z button1_clicked. B. ). Dieses Leerzeichen kann jedoch jede Methode haben, die Sie verdammt gut mögen . Sie können die Methode aufrufen run_computationsoder butter_the_biscuitssolange Sie den Namen dieses Rückrufs in das richtige Leerzeichen einfügen. Sie können "555-555-1212" in die Notrufnummer eingeben. Es macht nicht viel Sinn, aber es ist zulässig.


Letzte Anmerkung: Diese leere Zeile, die Sie mit dem Rückruf ausfüllen? Es kann nach Belieben gelöscht und neu geschrieben werden. (ob du solltest oder nicht, ist eine andere Frage, aber das ist ein Teil ihrer Macht)

JoeyG
quelle
21

Immer besser mit einem Beispiel beginnen :).

Angenommen, Sie haben zwei Module A und B.

Sie möchten, dass Modul A benachrichtigt wird, wenn ein Ereignis / eine Bedingung in Modul B auftritt. Modul B hat jedoch keine Ahnung von Ihrem Modul A. Es ist lediglich eine Adresse für eine bestimmte Funktion (von Modul A) über einen Funktionszeiger bekannt bereitgestellt von Modul A.

Alles, was B jetzt tun muss, ist ein "Rückruf" in Modul A, wenn ein bestimmtes Ereignis / eine bestimmte Bedingung mithilfe des Funktionszeigers auftritt. A kann innerhalb der Rückruffunktion weiterverarbeiten.

*) Ein klarer Vorteil hierbei ist, dass Sie alles über Modul A von Modul B abstrahieren. Modul B muss sich nicht darum kümmern, wer / was Modul A ist.

Gargi Srinivas
quelle
Die Parameter für die Funktion in A sind also im Modul B angegeben. Ist das richtig?
U.Savas
21

Johny, der Programmierer, braucht einen Hefter, also geht er zur Bürobedarfsabteilung und fragt nach einem. Nachdem er das Anforderungsformular ausgefüllt hat, kann er entweder dort stehen und warten, bis der Angestellte im Lager nach dem Hefter sucht (wie ein Anruf bei einer Sperrfunktion) ) oder mach in der Zwischenzeit etwas anderes.

Da dies normalerweise einige Zeit in Anspruch nimmt, stellt Johny zusammen mit dem Anfrageformular eine Notiz zusammen und bittet sie, ihn anzurufen, wenn der Hefter zur Abholung bereit ist, damit er in der Zwischenzeit etwas anderes tun kann, als auf seinem Schreibtisch ein Nickerchen zu machen.

tovmeod
quelle
1
Dies scheint eher Versprechungen als Rückrufen zu ähneln
Thomas
2
Versprechen sind nur syntaktischer Zucker um Rückrufe.
Deven Phillips
20

Stellen Sie sich vor, Sie benötigen eine Funktion, die 10 Quadrat zurückgibt, damit Sie eine Funktion schreiben:

function tenSquared() {return 10*10;}

Später benötigen Sie 9 Quadrate, um eine weitere Funktion zu schreiben:

function nineSquared() {return 9*9;}

Schließlich werden Sie alle diese durch eine generische Funktion ersetzen:

function square(x) {return x*x;}

Das gleiche Denken gilt für Rückrufe. Sie haben eine Funktion, die etwas tut, und wenn Sie fertig sind, rufen Sie doA auf:

function computeA(){
    ...
    doA(result);
}

Später möchten Sie, dass genau dieselbe Funktion doB aufruft, stattdessen können Sie die gesamte Funktion duplizieren:

function computeB(){
    ...
    doB(result);
}

Oder Sie können eine Rückruffunktion als Variable übergeben und müssen die Funktion nur einmal haben:

function compute(callback){
    ...
    callback(result);
}

Dann müssen Sie nur noch compute (doA) und compute (doB) aufrufen.

Abgesehen von der Vereinfachung des Codes können Sie durch asynchronen Code wissen, dass der Vorgang abgeschlossen ist, indem Sie nach Abschluss Ihre beliebige Funktion aufrufen, ähnlich wie wenn Sie jemanden am Telefon anrufen und eine Rückrufnummer hinterlassen.

Brian Nickel
quelle
Sie übergeben also die Funktion als Parameter. Erlauben alle Programmiersprachen die Übergabe der Funktion als Parameter? Wenn nein, können Sie ein Beispiel zeigen, wie Sie die Rückruffunktion in dieser Sprache implementieren.
Quazi Irfan
11

Sie fühlen sich krank und gehen zum Arzt. Er untersucht Sie und stellt fest, dass Sie Medikamente benötigen. Er verschreibt einige Medikamente und ruft das Rezept in Ihrer örtlichen Apotheke an. Du gehst nach Hause. Später ruft Ihre Apotheke an, um Ihnen mitzuteilen, dass Ihr Rezept fertig ist. Du gehst und holst es ab.

Bildnis
quelle
Sehr gute Analogie. Könnten Sie bitte etwas weiter ausbauen, vielleicht mit einigen (einfachen) programmierbezogenen Beispielen?
a20
10

Es gibt zwei Punkte zu erklären: Zum einen die Funktionsweise eines Rückrufs (Weitergabe einer Funktion, die ohne Kenntnis des Kontexts aufgerufen werden kann) und zum anderen die asynchrone Behandlung von Ereignissen.

Die Analogie, auf das Eintreffen eines Pakets zu warten, das von anderen Antworten verwendet wurde, ist gut, um beide zu erklären. In einem Computerprogramm würden Sie den Computer anweisen, ein Paket zu erwarten. Normalerweise würde es jetzt dort sitzen und warten (und nichts anderes tun), bis das Paket ankommt, möglicherweise auf unbestimmte Zeit, wenn es nie ankommt. Für den Menschen klingt das albern, aber ohne weitere Maßnahmen ist dies für einen Computer völlig natürlich.

Jetzt wäre der Rückruf die Glocke an Ihrer Haustür. Sie bieten dem Paketservice die Möglichkeit, Sie über die Ankunft des Pakets zu informieren, ohne dass dieser wissen muss, wo (auch wenn) Sie sich im Haus befinden oder wie die Glocke funktioniert. (Zum Beispiel senden einige "Glocken" tatsächlich einen Anruf aus.) Da Sie eine "Rückruffunktion" bereitgestellt haben, die jederzeit außerhalb des Kontexts "aufgerufen" werden kann, können Sie jetzt aufhören, auf der Veranda zu sitzen, und "das erledigen" Ereignis "(der Paketankunft) wann immer es Zeit ist.

Hanno Fietz
quelle
Das ist einfaches Englisch.
Jeb50
Dies scheint die beste Erklärung zu sein!
Vishal Sharma
6

Stellen Sie sich vor, eine Freundin verlässt Ihr Haus und Sie sagen ihr: "Rufen Sie mich an, wenn Sie nach Hause kommen, damit ich weiß, dass Sie sicher angekommen sind." das ist (buchstäblich) ein Rückruf . Das ist eine Rückruffunktion, unabhängig von der Sprache. Sie möchten, dass eine Prozedur die Kontrolle an Sie zurückgibt, wenn sie eine Aufgabe abgeschlossen hat, und geben ihr eine Funktion, mit der Sie zurückgerufen werden können.

In Python zum Beispiel

grabDBValue( (lambda x: passValueToGUIWindow(x) ))

grabDBValuekönnte so geschrieben werden, dass nur ein Wert aus einer Datenbank abgerufen wird und Sie dann angeben können, was mit dem Wert tatsächlich geschehen soll, sodass eine Funktion akzeptiert wird. Sie wissen nicht, wann oder ob Sie grabDBValuezurückkehren werden, aber wenn / wann dies der Fall ist, wissen Sie, was Sie möchten. Hier übergebe ich eine anonyme Funktion (oder Lambda ), die den Wert an ein GUI-Fenster sendet. Ich könnte das Verhalten des Programms leicht ändern, indem ich dies tue:

grabDBValue( (lambda x: passToLogger(x) ))

Rückrufe funktionieren gut in Sprachen, in denen Funktionen erstklassige Werte sind, genau wie die üblichen Ganzzahlen, Zeichenfolgen, Booleschen Werte usw. In C können Sie eine Funktion "übergeben", indem Sie einen Zeiger darauf übergeben, und der Aufrufer kann dies verwenden. In Java fragt der Aufrufer nach einer statischen Klasse eines bestimmten Typs mit einem bestimmten Methodennamen, da es außerhalb der Klassen keine Funktionen ("Methoden") gibt. und in den meisten anderen dynamischen Sprachen können Sie einfach eine Funktion mit einfacher Syntax übergeben.

Protip:

In Sprachen mit lexikalischem Umfang (wie Scheme oder Perl) können Sie einen Trick wie folgt ausführen :

my $var = 2;
my $val = someCallerBackFunction(sub callback { return $var * 3; });
# Perlistas note: I know the sub doesn't need a name, this is for illustration

$valIn diesem Fall 6hat der Rückruf Zugriff auf die Variablen, die in der lexikalischen Umgebung deklariert wurden, in der er definiert wurde. Lexikalischer Umfang und anonyme Rückrufe sind eine leistungsstarke Kombination, die dem unerfahrenen Programmierer weitere Studien rechtfertigt.

Gatlin
quelle
1
+1. Diese Antwort gefällt mir eigentlich ganz gut. Die Erklärung, was ein Rückruf ist , ist einfach und prägnant.
TarkaDaal
6

Sie haben Code, den Sie ausführen möchten. Wenn Sie es aufrufen, warten Sie normalerweise darauf, dass es fertig ist, bevor Sie fortfahren (was dazu führen kann, dass Ihre App grau wird / eine Drehzeit für einen Cursor erzeugt).

Eine alternative Methode besteht darin, diesen Code parallel auszuführen und mit Ihrer eigenen Arbeit fortzufahren. Aber was ist, wenn Ihr ursprünglicher Code abhängig von der Antwort des aufgerufenen Codes unterschiedliche Aufgaben ausführen muss? In diesem Fall können Sie den Namen / Speicherort des Codes eingeben, den er aufrufen soll, wenn er fertig ist. Dies ist ein "Rückruf".

Normaler Code: Fragen Sie nach Informationen-> Prozessinformationen-> Verarbeiten Sie die Ergebnisse der Verarbeitung-> Fahren Sie mit anderen Dingen fort.

Bei Rückrufen: Fragen Sie nach Informationen-> Prozessinformationen-> Fahren Sie mit anderen Dingen fort. Und zu einem späteren Zeitpunkt-> Behandeln Sie die Ergebnisse der Verarbeitung.

Andrew Ducker
quelle
6

Ohne Rückruf weder andere spezielle Programmierressourcen (wie Threading und andere) ist ein Programm genau eine Folge von Anweisungen, die nacheinander ausgeführt werden , und selbst mit einer Art "dynamischem Verhalten", das durch bestimmte Bedingungen bestimmt wird, alle möglichen Szenarien muss vorher programmiert sein .

Wenn wir also einem Programm ein wirklich dynamisches Verhalten verleihen müssen, können wir einen Rückruf verwenden. Mit Callback können Sie durch Parameter anweisen, ein Programm ein anderes Programm aufzurufen, das einige zuvor definierte Parameter bereitstellt, und einige Ergebnisse erwarten ( dies ist die Vertrags- oder Operationssignatur ), sodass diese Ergebnisse von einem Drittanbieterprogramm erzeugt / verarbeitet werden können, das nicht vorhanden war ist bisher nicht bekannt.

Diese Technik ist die Grundlage des Polymorphismus, der auf Programme, Funktionen, Objekte und alle anderen von Computern ausgeführten Codeeinheiten angewendet wird.

Die menschliche Welt, die als Beispiel für den Rückruf verwendet wird, wird schön erklärt, wenn Sie einen Job machen. Nehmen wir an, Sie sind Maler ( hier sind Sie das Hauptprogramm, das malt ) und rufen Sie Ihren Kunden manchmal an, um ihn zu bitten, das Ergebnis Ihres Jobs zu genehmigen Daher entscheidet er, ob das Bild gut ist ( Ihr Kunde ist das Programm eines Drittanbieters ).

Im obigen Beispiel sind Sie Maler und "delegieren" den Auftrag an andere, um das Ergebnis zu genehmigen. Das Bild ist der Parameter, und jeder neue Kunde (die zurückgerufene "Funktion") ändert das Ergebnis Ihrer Arbeit und entscheidet, was er will über das Bild ( die Entscheidung der Kunden ist das zurückgegebene Ergebnis der "Rückruffunktion" ).

Ich hoffe, diese Erklärung kann nützlich sein.

Luciano
quelle
6

Stellen wir uns vor, Sie würden mir eine möglicherweise langfristige Aufgabe geben: Erhalten Sie die Namen der ersten fünf einzigartigen Personen, denen Sie begegnen. Dies kann Tage dauern, wenn ich mich in einem dünn besiedelten Gebiet befinde. Sie sind nicht wirklich daran interessiert, auf Ihren Händen zu sitzen, während ich herumlaufe. Sie sagen also: "Wenn Sie die Liste haben, rufen Sie mich in meinem Handy an und lesen Sie sie mir vor. Hier ist die Nummer."

Sie haben mir eine Rückrufreferenz gegeben - eine Funktion, die ich ausführen soll, um die weitere Verarbeitung abzugeben.

In JavaScript könnte es ungefähr so ​​aussehen:

var lottoNumbers = [];
var callback = function(theNames) {
  for (var i=0; i<theNames.length; i++) {
    lottoNumbers.push(theNames[i].length);
  }
};

db.executeQuery("SELECT name " +
                "FROM tblEveryOneInTheWholeWorld " +
                "ORDER BY proximity DESC " +
                "LIMIT 5", callback);

while (lottoNumbers.length < 5) {
  playGolf();
}
playLotto(lottoNumbers);

Dies könnte wahrscheinlich in vielerlei Hinsicht verbessert werden. Sie können beispielsweise einen zweiten Rückruf bereitstellen: Wenn dies länger als eine Stunde dauert, rufen Sie das rote Telefon an und teilen Sie der antwortenden Person mit, dass Sie eine Zeitüberschreitung haben.

Dampfer25
quelle
6

Rückrufe lassen sich am einfachsten anhand des Telefonsystems beschreiben. Ein Funktionsaufruf ist analog dazu, jemanden am Telefon anzurufen, ihr eine Frage zu stellen, eine Antwort zu erhalten und aufzulegen. Durch Hinzufügen eines Rückrufs wird die Analogie geändert, sodass Sie ihr nach dem Stellen einer Frage auch Ihren Namen und Ihre Nummer geben, damit sie Sie mit der Antwort zurückrufen kann. - Paul Jakubik, "Callback-Implementierungen in C ++"

DejanLekic
quelle
Eine einfachere Erklärung wäre: Ich rufe jemanden an, sie ist in einer Besprechung, ich hinterlasse eine Telefonnummer, sie ruft zurück.
Einsam
5

Ein Rückruf ist eine Funktion, die von einer zweiten Funktion aufgerufen wird. Diese zweite Funktion weiß nicht im Voraus, welche Funktion sie aufruft. Die Identität der Rückruffunktion wird also irgendwo gespeichert oder als Parameter an die zweite Funktion übergeben. Diese "Identität" kann abhängig von der Programmiersprache die Adresse des Rückrufs oder eine andere Art von Zeiger oder der Name der Funktion sein. Der Principal ist derselbe. Wir speichern oder übergeben einige Informationen, die die Funktion eindeutig identifizieren.

Wenn es soweit ist, kann die zweite Funktion den Rückruf aufrufen und abhängig von den Umständen in diesem Moment Parameter bereitstellen. Möglicherweise wird der Rückruf sogar aus einer Reihe möglicher Rückrufe ausgewählt. Die Programmiersprache muss eine Art Syntax bereitstellen, damit die zweite Funktion den Rückruf aufrufen kann, wobei ihre "Identität" bekannt ist.

Dieser Mechanismus hat sehr viele Verwendungsmöglichkeiten. Bei Rückrufen kann der Designer einer Funktion die Anpassung vornehmen, indem er die bereitgestellten Rückrufe aufruft. Beispielsweise kann eine Sortierfunktion einen Rückruf als Parameter verwenden, und dieser Rückruf kann eine Funktion zum Vergleichen zweier Elemente sein, um zu entscheiden, welches zuerst kommt.

Übrigens kann abhängig von der Programmiersprache das Wort "Funktion" in der obigen Diskussion durch "Block", "Schließung", "Lambda" usw. ersetzt werden.

David Casseres
quelle
5

Normalerweise haben wir Variablen an Funktionen gesendet. Angenommen, Sie haben eine Aufgabe, bei der die Variable verarbeitet werden muss, bevor sie als Argument angegeben wird. Sie können den Rückruf verwenden.

function1(var1, var2) ist der übliche Weg.

Was ist, wenn ich var2verarbeitet und dann als Argument gesendet werden möchte ? function1(var1, function2(var2))

Dies ist eine Art von Rückruf, bei dem function2Code ausgeführt und eine Variable an die ursprüngliche Funktion zurückgegeben wird.

Nishant
quelle
2
Was ist eine andere Art von Rückruf?
Johnny
@ Johnny: Es gibt normale Browser-Rückrufe, die ausgelöst werden, wenn ein Ajax abgeschlossen ist usw.
Nishant
4

Eine metaphorische Erklärung:

Ich habe ein Paket, das ich an einen Freund liefern möchte, und ich möchte auch wissen, wann mein Freund es erhält.

Also bringe ich das Paket zur Post und bitte sie, es zu liefern. Wenn ich wissen möchte, wann mein Freund das Paket erhält, habe ich zwei Möglichkeiten:

(a) Ich kann bei der Post warten, bis es zugestellt wird.

(b) Ich werde eine E-Mail erhalten, wenn sie zugestellt wird.

Option (b) ist analog zu einem Rückruf.

Tonylo
quelle
4

Um Rückrufe zu lehren, müssen Sie zuerst den Zeiger lehren. Sobald die Schüler die Idee des Zeigers auf eine Variable verstanden haben, wird die Idee von Rückrufen einfacher. Angenommen, Sie verwenden C / C ++, können diese Schritte ausgeführt werden.

  • Zeigen Sie Ihren Schülern zunächst, wie Sie Variablen mithilfe von Zeigern neben den normalen Variablenbezeichnern verwenden und bearbeiten.
  • Bringen Sie ihnen dann bei, dass es Dinge gibt, die nur mit Zeigern ausgeführt werden können (z. B. das Übergeben einer Variablen als Referenz).
  • Sagen Sie ihnen dann, wie ausführbarer Code oder ausführbare Funktionen wie andere Daten (oder Variablen) im Speicher sind. Funktionen haben also auch Adressen oder Zeiger.
  • Zeigen Sie ihnen dann, wie Funktionen mit Funktionszeigern aufgerufen werden können, und teilen Sie ihnen mit, dass diese Rückrufe genannt werden.
  • Die Frage ist nun, warum all diese Probleme beim Aufrufen einiger Funktionen auftreten. Was ist der Vorteil? Wie Datenzeiger hat der Funktionszeiger, auch Rückrufe genannt, einige Vorteile gegenüber der Verwendung normaler Bezeichner.
  • Der erste ist, dass Funktionskennungen oder Funktionsnamen nicht als normale Daten verwendet werden können. Ich meine, Sie können keine Datenstruktur mit Funktionen erstellen (wie ein Array oder eine verknüpfte Liste von Funktionen). Mit Rückrufen können Sie jedoch ein Array oder eine verknüpfte Liste erstellen oder diese mit anderen Daten verwenden, z. B. im Wörterbuch der Schlüssel-Wert-Paare oder Bäume oder mit anderen Dingen. Dies ist ein starker Vorteil. Und andere Vorteile sind tatsächlich ein Kind von diesem.
  • Die häufigste Verwendung von Rückrufen ist die Programmierung von Ereignistreibern. Wenn eine oder mehrere Funktionen basierend auf einem eingehenden Signal ausgeführt werden. Mit Rückrufen kann ein Wörterbuch verwaltet werden, um Signale mit Rückrufen abzubilden. Dann wird die Auflösung des Eingangssignals und die Ausführung des entsprechenden Codes viel einfacher.
  • Die zweite Verwendung von Rückrufen, die mir in den Sinn kommen, sind Funktionen höherer Ordnung. Die Funktionen, die andere Funktionen als Eingabeargumente verwenden. Und um Funktionen als Argumente zu senden, brauchen wir Rückrufe. Ein Beispiel kann eine Funktion sein, die ein Array und einen Rückruf akzeptiert. Anschließend führt es den Rückruf für jedes Element des Arrays durch und gibt die Ergebnisse in einem anderen Array zurück. Wenn wir der Funktion einen doppelten Rückruf übergeben, erhalten wir ein Array mit doppeltem Wert. Wenn wir einen quadratischen Rückruf bestehen, erhalten wir Quadrate. Für Quadratwurzeln senden Sie einfach den entsprechenden Rückruf. Dies ist mit normalen Funktionen nicht möglich.

Es könnte noch viel mehr Dinge geben. Beziehen Sie die Schüler ein und sie werden es entdecken. Hoffe das hilft.

Gulshan
quelle
1
Meine andere Antwort bezog sich auf dieses Thema in programmers.SE programmers.stackexchange.com/a/75449/963
Gulshan
3

Im Klartext ist ein Rückruf ein Versprechen. Joe, Jane, David und Samantha teilen sich eine Fahrgemeinschaft, um zu arbeiten. Joe fährt heute. Jane, David und Samantha haben mehrere Möglichkeiten:

  1. Überprüfen Sie das Fenster alle 5 Minuten, um festzustellen, ob Joe draußen ist
  2. Mach weiter, bis Joe an der Tür klingelt.

Option 1: Dies ist eher ein Umfragebeispiel, bei dem Jane in einer "Schleife" stecken bleibt und prüft, ob Joe draußen ist. Jane kann in der Zwischenzeit nichts anderes tun.

Option 2: Dies ist das Rückrufbeispiel. Jane fordert Joe auf, an der Tür zu klingeln, wenn er draußen ist. Sie gibt ihm eine "Funktion", um die Türklingel zu läuten. Joe muss nicht wissen, wie die Türklingel funktioniert oder wo sie ist, er muss nur diese Funktion aufrufen, dh die Türklingel läuten, wenn er dort ist.

Rückrufe werden durch "Ereignisse" ausgelöst. In diesem Beispiel ist das "Ereignis" Joes Ankunft. In Ajax können Ereignisse beispielsweise "Erfolg" oder "Misserfolg" der asynchronen Anforderung sein und jeder kann den gleichen oder unterschiedliche Rückrufe haben.

In Bezug auf JavaScript-Anwendungen und Rückrufe. Wir müssen auch "Schließungen" und den Anwendungskontext verstehen. Worauf sich "dies" bezieht, kann JavaScript-Entwickler leicht verwirren. In diesem Beispiel gibt es innerhalb der Methode / des Rückrufs "ring_the_door_bell ()" jeder Person möglicherweise einige andere Methoden, die jede Person basierend auf ihrer morgendlichen Routine ausführen muss. "Schalte den Fernseher aus()". Wir möchten, dass sich "dies" auf das "Jane" -Objekt oder das "David" -Objekt bezieht, damit jeder das einrichten kann, was er sonst noch tun muss, bevor Joe sie aufnimmt. Hier muss für das Einrichten des Rückrufs mit Joe die Methode parodiert werden, damit sich "this" auf das richtige Objekt bezieht.

Ich hoffe, das hilft!

Nael El Shawwa
quelle
3

Ein Rückruf ist ein an sich selbst adressierter Briefumschlag. Wenn Sie eine Funktion aufrufen, ist das wie das Senden eines Briefes. Wenn Sie möchten, dass diese Funktion eine andere Funktion aufruft, geben Sie diese Informationen in Form einer Referenz oder Adresse an.

pete
quelle
3

Ich denke, es ist eine ziemlich einfache Aufgabe zu erklären.

Beim ersten Rückruf handelt es sich nur um gewöhnliche Funktionen.
Und das weitere ist, dass wir diese Funktion (nennen wir sie A) aus einer anderen Funktion heraus (nennen wir sie B) aufrufen.

Das Magische daran ist, dass ich entscheide, welche Funktion von der Funktion von außen aufgerufen werden soll B

Zum Zeitpunkt, an dem ich die Funktion BI schreibe, weiß ich nicht, welche Rückruffunktion aufgerufen werden soll. Zu dem Zeitpunkt, an dem ich die Funktion BI aufrufe, muss diese Funktion auch die Funktion A aufrufen. Das ist alles.

Yunzen
quelle
3

Was ist eine Rückruffunktion?

Die einfache Antwort auf diese erste Frage lautet, dass eine Rückruffunktion eine Funktion ist, die über einen Funktionszeiger aufgerufen wird. Wenn Sie den Zeiger (die Adresse) einer Funktion als Argument an eine andere übergeben, wird beim Aufrufen der Funktion, auf die dieser Zeiger verweist, darauf hingewiesen, dass ein Rückruf erfolgt.

Die Rückruffunktion ist schwer zu verfolgen, aber manchmal sehr nützlich. Besonders wenn Sie Bibliotheken entwerfen. Die Rückruffunktion entspricht der Aufforderung an Ihren Benutzer, Ihnen einen Funktionsnamen zu geben, und Sie rufen diese Funktion unter bestimmten Bedingungen auf.

Beispielsweise schreiben Sie einen Rückruf-Timer. Hier können Sie die Dauer und die aufzurufende Funktion angeben, und die Funktion wird entsprechend zurückgerufen. "Führen Sie myfunction () 5 Mal alle 10 Sekunden aus."

Sie können auch ein Funktionsverzeichnis erstellen, eine Liste mit Funktionsnamen übergeben und die Bibliothek auffordern, einen entsprechenden Rückruf durchzuführen. "Rückruf erfolgreich () wenn erfolgreich, Rückruf fehlgeschlagen () wenn fehlgeschlagen."

Schauen wir uns ein einfaches Beispiel für einen Funktionszeiger an

void cbfunc()
{
     printf("called");
}

 int main ()
 {
                   /* function pointer */ 
      void (*callback)(void); 
                   /* point to your callback function */ 
      callback=(void *)cbfunc; 
                   /* perform callback */
      callback();
      return 0; 
}

Wie übergebe ich ein Argument an die Rückruffunktion?

Es wurde beobachtet, dass der Funktionszeiger zum Implementieren eines Rückrufs void * enthält, was darauf hinweist, dass er jede Art von Variable einschließlich Struktur aufnehmen kann. Daher können Sie mehrere Argumente nach Struktur übergeben.

typedef struct myst
{
     int a;
     char b[10];
}myst;

void cbfunc(myst *mt) 
{
     fprintf(stdout,"called %d %s.",mt->a,mt->b); 
}

int main() 
{
       /* func pointer */
    void (*callback)(void *);       //param
     myst m;
     m.a=10;
     strcpy(m.b,"123");       
     callback = (void*)cbfunc;    /* point to callback function */
     callback(&m);                /* perform callback and pass in the param */
     return 0;   
}
Sachin Mhetre
quelle
2

Ein Rückruf ist eine Methode, deren Ausführung geplant ist, wenn eine Bedingung erfüllt ist.

Ein Beispiel aus der "realen Welt" ist ein lokaler Videospielladen. Sie warten auf Half-Life 3. Anstatt jeden Tag in den Laden zu gehen, um zu sehen, ob es in ist, registrieren Sie Ihre E-Mail in einer Liste, um benachrichtigt zu werden, wenn das Spiel verfügbar ist. Die E-Mail wird zu Ihrem "Rückruf" und die Bedingung, die erfüllt werden muss, ist die Verfügbarkeit des Spiels.

Ein Beispiel für "Programmierer" ist eine Webseite, auf der Sie eine Aktion ausführen möchten, wenn Sie auf eine Schaltfläche klicken. Sie registrieren eine Rückrufmethode für eine Schaltfläche und führen andere Aufgaben aus. Wenn der Benutzer auf die Schaltfläche klickt, zeigt der Browser die Liste der Rückrufe für dieses Ereignis an und ruft Ihre Methode auf.

Ein Rückruf ist eine Möglichkeit, Ereignisse asynchron zu behandeln. Sie können nie wissen, wann der Rückruf ausgeführt wird oder ob er überhaupt ausgeführt wird. Der Vorteil ist, dass Ihr Programm und Ihre CPU-Zyklen frei sind, um andere Aufgaben auszuführen, während Sie auf die Antwort warten.

Optimist
quelle
Zu sagen, dass es "geplant" ist, könnte hier Verwirrung stiften. Rückrufe werden häufig in asynchronen Systemen verwendet, und es gibt keinen "Zeitplan", sondern ein "Ereignis", das die Ausführung des Rückrufs auslöst.
Deven Phillips
2

Schlicht und einfach: Ein Rückruf ist eine Funktion, die Sie einer anderen Funktion geben, damit diese aufgerufen werden kann.

Normalerweise wird es aufgerufen, wenn ein Vorgang abgeschlossen ist. Da Sie den Rückruf erstellen, bevor Sie ihn der anderen Funktion übergeben, können Sie ihn mit Kontextinformationen von der Aufrufseite initialisieren. Aus diesem Grund wird es als Rückruf * zurück * bezeichnet - die erste Funktion ruft den Kontext zurück, von dem aus sie aufgerufen wurde.

Andrei Vajna II
quelle
2

„In der Computerprogrammierung ist ein Rückruf eine Referenz auf ausführbaren Code oder einen ausführbaren Code, der als Argument an anderen Code übergeben wird. Auf diese Weise kann eine untergeordnete Softwareschicht eine Unterroutine (oder Funktion) aufrufen, die in einer übergeordneten Ebene definiert ist. “ - Wikipedia

Rückruf in C mit Funktionszeiger

In C wird der Rückruf mit dem Funktionszeiger implementiert. Funktionszeiger - ist, wie der Name schon sagt, ein Zeiger auf eine Funktion.

Zum Beispiel int (* ptrFunc) ();

Hier ist ptrFunc ein Zeiger auf eine Funktion, die keine Argumente akzeptiert und eine Ganzzahl zurückgibt. Vergessen Sie NICHT, in Klammern zu setzen, sonst geht der Compiler davon aus, dass ptrFunc ein normaler Funktionsname ist, der nichts nimmt und einen Zeiger auf eine Ganzzahl zurückgibt.

Hier ist ein Code zur Demonstration des Funktionszeigers.

#include<stdio.h>
int func(int, int);
int main(void)
{
    int result1,result2;
    /* declaring a pointer to a function which takes
       two int arguments and returns an integer as result */
    int (*ptrFunc)(int,int);

    /* assigning ptrFunc to func's address */                    
    ptrFunc=func;

    /* calling func() through explicit dereference */
    result1 = (*ptrFunc)(10,20);

    /* calling func() through implicit dereference */        
    result2 = ptrFunc(10,20);            
    printf("result1 = %d result2 = %d\n",result1,result2);
    return 0;
}

int func(int x, int y)
{
    return x+y;
}

Versuchen wir nun, das Konzept des Rückrufs in C mithilfe des Funktionszeigers zu verstehen.

Das vollständige Programm enthält drei Dateien: callback.c, reg_callback.h und reg_callback.c.

/* callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* callback function definition goes here */
void my_callback(void)
{
    printf("inside my_callback\n");
}

int main(void)
{
    /* initialize function pointer to
    my_callback */
    callback ptr_my_callback=my_callback;                        
    printf("This is a program demonstrating function callback\n");
    /* register our callback function */
    register_callback(ptr_my_callback);                          
    printf("back inside main program\n");
    return 0;
}

/* reg_callback.h */
typedef void (*callback)(void);
void register_callback(callback ptr_reg_callback);


/* reg_callback.c */
#include<stdio.h>
#include"reg_callback.h"

/* registration goes here */
void register_callback(callback ptr_reg_callback)
{
    printf("inside register_callback\n");
    /* calling our callback function my_callback */
    (*ptr_reg_callback)();                               
}

Wenn wir dieses Programm ausführen, wird die Ausgabe sein

Dies ist ein Programm, das den Funktionsrückruf in register_callback in my_callback zurück im Hauptprogramm demonstriert

Die Funktion der höheren Schicht ruft eine Funktion der niedrigeren Schicht als normalen Aufruf auf, und der Rückrufmechanismus ermöglicht es der Funktion der unteren Schicht, die Funktion der höheren Schicht über einen Zeiger auf eine Rückruffunktion aufzurufen.

Rückruf in Java über die Schnittstelle

Java hat nicht das Konzept eines Funktionszeigers. Es implementiert den Rückrufmechanismus über seinen Schnittstellenmechanismus. Hier deklarieren wir anstelle eines Funktionszeigers eine Schnittstelle mit einer Methode, die aufgerufen wird, wenn der Angerufene seine Aufgabe beendet

Lassen Sie es mich anhand eines Beispiels demonstrieren:

Die Rückrufschnittstelle

public interface Callback
{
    public void notify(Result result);
}

Der Anrufer oder die höhere Klasse

public Class Caller implements Callback
{
Callee ce = new Callee(this); //pass self to the callee

//Other functionality
//Call the Asynctask
ce.doAsynctask();

public void notify(Result result){
//Got the result after the callee has finished the task
//Can do whatever i want with the result
}
}

Die Callee oder die untere Schichtfunktion

public Class Callee {
Callback cb;
Callee(Callback cb){
this.cb = cb;
}

doAsynctask(){
//do the long running task
//get the result
cb.notify(result);//after the task is completed, notify the caller
}
}

Rückruf mit EventListener-Muster

  • Listenpunkt

Dieses Muster wird verwendet, um 0 bis n Anzahlen von Beobachtern / Zuhörern zu benachrichtigen, dass eine bestimmte Aufgabe beendet wurde

  • Listenpunkt

Der Unterschied zwischen dem Rückrufmechanismus und dem EventListener / Observer-Mechanismus besteht darin, dass der Angerufene beim Rückruf den einzelnen Anrufer benachrichtigt, während der Angerufene bei Eventlisener / Observer jeden benachrichtigen kann, der an diesem Ereignis interessiert ist (die Benachrichtigung kann an einige andere Teile des Anwendung, die die Aufgabe nicht ausgelöst hat)

Lassen Sie es mich anhand eines Beispiels erklären.

Die Ereignisschnittstelle

public interface Events {

public void clickEvent();
public void longClickEvent();
}

Klassen-Widget

package com.som_itsolutions.training.java.exampleeventlistener;

import java.util.ArrayList;
import java.util.Iterator;

public class Widget implements Events{

    ArrayList<OnClickEventListener> mClickEventListener = new ArrayList<OnClickEventListener>(); 
    ArrayList<OnLongClickEventListener> mLongClickEventListener = new ArrayList<OnLongClickEventListener>();

    @Override
    public void clickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnClickEventListener> it = mClickEventListener.iterator();
                while(it.hasNext()){
                    OnClickEventListener li = it.next();
                    li.onClick(this);
                }   
    }
    @Override
    public void longClickEvent() {
        // TODO Auto-generated method stub
        Iterator<OnLongClickEventListener> it = mLongClickEventListener.iterator();
        while(it.hasNext()){
            OnLongClickEventListener li = it.next();
            li.onLongClick(this);
        }

    }

    public interface OnClickEventListener
    {
        public void onClick (Widget source);
    }

    public interface OnLongClickEventListener
    {
        public void onLongClick (Widget source);
    }

    public void setOnClickEventListner(OnClickEventListener li){
        mClickEventListener.add(li);
    }
    public void setOnLongClickEventListner(OnLongClickEventListener li){
        mLongClickEventListener.add(li);
    }
}

Klassenschaltfläche

public class Button extends Widget{
private String mButtonText;
public Button (){
} 
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}

Klassen-Kontrollkästchen

public class CheckBox extends Widget{
private boolean checked;
public CheckBox() {
checked = false;
}
public boolean isChecked(){
return (checked == true);
}
public void setCheck(boolean checked){
this.checked = checked;
}
}

Aktivitätsklasse

Paket com.som_itsolutions.training.java.exampleeventlistener;

public class Activity implements Widget.OnClickEventListener
{
    public Button mButton;
    public CheckBox mCheckBox;
    private static Activity mActivityHandler;
    public static Activity getActivityHandle(){
        return mActivityHandler;
    }
    public Activity ()
    {
        mActivityHandler = this;
        mButton = new Button();
        mButton.setOnClickEventListner(this);
        mCheckBox = new CheckBox();
        mCheckBox.setOnClickEventListner(this);
        } 
    public void onClick (Widget source)
    {
        if(source == mButton){
            mButton.setButtonText("Thank you for clicking me...");
            System.out.println(((Button) mButton).getButtonText());
        }
        if(source == mCheckBox){
            if(mCheckBox.isChecked()==false){
                mCheckBox.setCheck(true);
                System.out.println("The checkbox is checked...");
            }
            else{
                mCheckBox.setCheck(false);
                System.out.println("The checkbox is not checked...");
            }       
        }
    }
    public void doSomeWork(Widget source){
        source.clickEvent();
    }   
}

Andere Klasse

public class OtherClass implements Widget.OnClickEventListener{
Button mButton;
public OtherClass(){
mButton = Activity.getActivityHandle().mButton;
mButton.setOnClickEventListner(this);//interested in the click event                        //of the button
}
@Override
public void onClick(Widget source) {
if(source == mButton){
System.out.println("Other Class has also received the event notification...");
}
}

Hauptklasse

public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Activity a = new Activity();
OtherClass o = new OtherClass();
a.doSomeWork(a.mButton);
a.doSomeWork(a.mCheckBox);
}
}

Wie Sie dem obigen Code entnehmen können, haben wir eine Schnittstelle namens events, die im Grunde alle Ereignisse auflistet, die für unsere Anwendung auftreten können. Die Widget-Klasse ist die Basisklasse für alle UI-Komponenten wie Button, Checkbox. Diese UI-Komponenten sind die Objekte, die die Ereignisse tatsächlich vom Framework-Code empfangen. Die Widget-Klasse implementiert die Ereignisschnittstelle und verfügt außerdem über zwei verschachtelte Schnittstellen, nämlich OnClickEventListener und OnLongClickEventListener

Diese beiden Schnittstellen sind für das Abhören von Ereignissen verantwortlich, die auf den vom Widget abgeleiteten UI-Komponenten wie Button oder Checkbox auftreten können. Wenn wir dieses Beispiel mit dem früheren Callback-Beispiel unter Verwendung der Java-Schnittstelle vergleichen, fungieren diese beiden Schnittstellen als Callback-Schnittstelle. Der übergeordnete Code (Here Activity) implementiert diese beiden Schnittstellen. Und wenn ein Ereignis in einem Widget auftritt, wird der Code auf höherer Ebene (oder die Methode dieser Schnittstellen, die im Code auf höherer Ebene implementiert ist, der hier Aktivität ist) aufgerufen.

Lassen Sie mich nun den grundlegenden Unterschied zwischen Callback- und Eventlistener-Muster diskutieren. Wie bereits erwähnt, kann der Callee mit Callback nur einen einzigen Anrufer benachrichtigen. Im Fall eines EventListener-Musters kann sich jedoch jeder andere Teil oder jede andere Klasse der Anwendung für die Ereignisse registrieren, die auf der Schaltfläche oder dem Kontrollkästchen auftreten können. Das Beispiel für diese Art von Klasse ist die OtherClass. Wenn Sie den Code der OtherClass sehen, werden Sie feststellen, dass sie sich als Listener für das ClickEvent registriert hat, das in der in der Aktivität definierten Schaltfläche auftreten kann. Interessant ist, dass neben der Aktivität (dem Anrufer) auch diese andere Klasse benachrichtigt wird, wenn das Klickereignis auf der Schaltfläche auftritt.

Somenath Mukhopadhyay
quelle
1

Mit Callbacks können Sie Ihren eigenen Code in einen anderen Codeblock einfügen, der zu einem anderen Zeitpunkt ausgeführt werden soll. Dadurch wird das Verhalten dieses anderen Codeblocks an Ihre Anforderungen angepasst oder ergänzt. Sie erhalten Flexibilität und Anpassbarkeit, während Sie in der Lage sind, mehr wartbaren Code zu haben.

Weniger Hardcode = einfacher zu warten und zu ändern = weniger Zeit = mehr Geschäftswert = Attraktivität.

In Javascript können Sie beispielsweise mit Underscore.js alle geraden Elemente in einem Array wie dem folgenden finden:

var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]

Beispiel mit freundlicher Genehmigung von Underscore.js: http://documentcloud.github.com/underscore/#filter

Letuboy
quelle
1

[bearbeitet] Wenn wir zwei Funktionen haben, sagen wir FunktionA und FunktionB , wenn Funktioneine abhängt FunktionD .

dann rufen wir functionB als Rückruffunktion auf. Dies wird im Spring-Framework häufig verwendet.

Rückruffunktion Wikipedia Beispiel

Balaswamy Vaddeman
quelle
1

Stellen Sie sich eine Methode vor, die einem Mitarbeiter eine Aufgabe gibt. Eine einfache Aufgabe könnte folgende sein:

Solve these equations:
x + 2 = y
2 * x = 3 * y

Ihr Mitarbeiter rechnet fleißig und gibt Ihnen folgendes Ergebnis:

x = -6
y = -4

Aber Ihr Mitarbeiter hat ein Problem, er versteht Notationen nicht immer, wie ^, aber er versteht sie anhand ihrer Beschreibung. Wie zum Beispiel exponent. Jedes Mal, wenn er eines davon findet, erhalten Sie Folgendes zurück:

I don't understand "^"

Dazu müssen Sie Ihren gesamten Befehlssatz erneut schreiben, nachdem Sie Ihrem Kollegen erklärt haben, was der Charakter bedeutet, und er erinnert sich nicht immer zwischen den Fragen. Und er hat Schwierigkeiten, sich auch an Ihre Tipps zu erinnern, wie zum Beispiel mich einfach zu fragen. Er folgt jedoch immer Ihren schriftlichen Anweisungen, so gut er kann.

Wenn Sie an eine Lösung denken, fügen Sie einfach allen Ihren Anweisungen Folgendes hinzu:

If you have any questions about symbols, call me at extension 1234 and I will tell you its name.

Wenn er jetzt ein Problem hat, ruft er Sie an und fragt, anstatt Ihnen eine schlechte Antwort zu geben und den Prozess neu zu starten.

Guvante
quelle
0

Dies davon in Bezug auf das Herunterladen einer Webseite:

Ihr Programm läuft auf einem Mobiltelefon und fordert die Webseite http://www.google.com an . Wenn Sie Ihr Programm synchron schreiben, wird die Funktion, die Sie zum Herunterladen der Daten schreiben, kontinuierlich ausgeführt, bis alle Daten heruntergeladen sind. Dies bedeutet, dass Ihre Benutzeroberfläche nicht aktualisiert wird und grundsätzlich eingefroren erscheint. Wenn Sie Ihr Programm mit Rückrufen schreiben, fordern Sie die Daten an und sagen "Führen Sie diese Funktion aus, wenn Sie fertig sind." Auf diese Weise kann die Benutzeroberfläche weiterhin Benutzerinteraktionen zulassen, während die Datei heruntergeladen wird. Sobald der Download der Webseite abgeschlossen ist, wird Ihre Ergebnisfunktion (Rückruf) aufgerufen und Sie können die Daten verarbeiten.

Grundsätzlich können Sie etwas anfordern und die Ausführung fortsetzen, während Sie auf das Ergebnis warten. Sobald das Ergebnis über eine Rückruffunktion bei Ihnen eingeht, können Sie den Vorgang dort fortsetzen, wo er aufgehört hat.

sokket
quelle