Warum brauchen wir "Rückruffunktionen"?

16

Ich lese das Buch programming in Lua. Das hat es gesagt

Verschlüsse sind in vielen Zusammenhängen ein wertvolles Werkzeug. Wie wir gesehen haben, sind sie als Argumente für Funktionen höherer Ordnung wie sort nützlich. Closures sind wertvoll für Funktionen, die auch andere Funktionen erstellen, wie in unserem newCounter-Beispiel. Dieser Mechanismus ermöglicht es Lua-Programmen, ausgefeilte Programmiertechniken aus der Funktionswelt zu integrieren. Closures sind auch für Callback-Funktionen nützlich. Ein typisches Beispiel hierfür ist das Erstellen von Schaltflächen in einem herkömmlichen GUI-Toolkit. Jede Taste verfügt über eine Rückruffunktion, die aufgerufen wird, wenn der Benutzer die Taste drückt. Sie möchten, dass verschiedene Tasten beim Drücken etwas unterschiedliche Aktionen ausführen. Ein digitaler Taschenrechner benötigt beispielsweise zehn ähnliche Tasten, eine für jede Ziffer. Sie können jede davon mit einer Funktion wie der folgenden erstellen:

function digitButton (digit)
  return Button{label = tostring(digit),
                action = function ()
                  add_to_display(digit)
                  end}
end

Es scheint, dass, wenn ich das aufrufe digitButton, es das zurückgibt action(dies erzeugt einen Abschluss), so dass ich auf das digitübergebene zugreifen kann digitButton.

Meine Frage ist, dass:

Why we need call back functions? what situations can I apply this to?


Der Autor sagte:

In diesem Beispiel wird angenommen, dass Button eine Toolkit-Funktion ist, mit der neue Schaltflächen erstellt werden. label ist die Tastenbezeichnung; und action ist die Rückrufsperre, die beim Drücken der Taste aufgerufen wird. Der Rückruf kann lange Zeit, nachdem digitButton seine Aufgabe erfüllt hat und nachdem die Ziffer der lokalen Variablen den Gültigkeitsbereich verlassen hat, aufgerufen werden, kann jedoch weiterhin auf diese Variable zugegriffen werden.

Nach Meinung des Autors ist ein ähnliches Beispiel wie folgt:

function Button(t)
  -- maybe you should set the button here
  return t.action -- so that you can call this later
end

function add_to_display(digit)
  print ("Display the button label: " .. tostring(digit))
end

function digitButton(digit)
  return Button{label = tostring(digit),
                action = function ()
                           add_to_display(digit)
                           end}
end

click_action = digitButton(10)
click_action()

somit, the callback can be called a long time after digitButton did its task and after the local variable digit went out of scope.

Lucas Li
quelle

Antworten:

45

Kerl 1 bis Kerl 2: Hey Kerl, ich möchte etwas tun, wenn ein Benutzer dort einklickt, mich zurückrufen, wenn das in Ordnung ist?

Guy 2 ruft Guy 1 zurück, wenn ein Benutzer hier klickt.

Florian Margaine
quelle
1
Ich mag diesen Rückruf-Witz sehr :-P
Lucas Li
6
Bin es nur ich Ich verstehe nicht, wie dies erklärt, warum wir Rückruffunktionen benötigen.
Phant0m
2
Es ist eher so, als ob Guy 1 zu Guy 2 sagt "wenn die Zeit gekommen ist, mach das". Guy 2 geht seinem Tag nach und wenn die Zeit reif ist, tut er das auch. Wenn Sie gemeint haben, dass Guy 1 der Anrufer von Guy 2 ist, dann ist dies falsch, da Guy 2 Guy 1 nicht zurückruft, sondern in die Aufgabe einberuft. Wenn Guy 1 der Anrufer von Guy 2 ist, haben Sie in diesem Szenario eine harte Schleife in der Hand. Wenn Sie gemeint haben, dass Guy 1 der Rückruf ist, ist dies falsch, da der Rückruf keine Ahnung hat, wer Guy 2 ist, und ihn mit Sicherheit nicht auffordert, anzurufen.
Risma
Das ist eine schreckliche Erklärung.
Insidesin
21

Nein, es wird nie zurückkehren die Aktion. Die Schaltfläche wird ausgeführt , wenn der Benutzer darauf klickt. Und das ist die Antwort. Sie benötigen Rückruffunktionen, wenn Sie Aktionen definieren möchten, die in einer anderen Komponente als Reaktion auf ein Ereignis, das Sie nicht steuern, ausgeführt werden sollen (die Ereignisschleife, die Teil des Systems ist, führt eine Methode der Schaltfläche aus, die wiederum ausgeführt wird) die Aktion).

Jan Hudec
quelle
Ich stimme dir nicht zu. Ich habe meinen Beitrag bearbeitet und einige Codes hinzugefügt. Ich denke immer noch, dass die Aktion nicht sofort ausgeführt wird.
Lucas Li
2
Tim, du hast recht - aber Jan auch. "Die Schaltfläche wird ausgeführt, wenn der Benutzer darauf klickt", nicht sofort.
AlexanderBrevig
@AlexanderBrevig Ja, ich denke immer noch, Jan's Antwort ist sehr nützlich für mich. Es lässt mich wissen, was Rückruf ist und warum wir Rückruf brauchen.
Lucas Li
Rückruf ist nur ein unsauberer Name, den jemand zu einer zweiten Funktion hinzugefügt hat, die funktioniert, wenn die erste ausgelöst wird und möglicherweise auf einem Kriterium basiert. Rufe deine Funktion well_done () auf und hätte keinen Unterschied. Rückruf ist wie Überprüfen, Überprüfen ist eine Folge von Nachrichten, normalerweise zwei, und Rückruf ist eine Folge von Funktionen, normalerweise zwei. Wenn Sie mehr als 3 Rückrufe angekettet haben, möchten Sie die Dinge auf die harte Tour machen.
m3nda
16

Ich denke, ein besseres Beispiel für die Verwendung von Rückrufen sind asynchrone Funktionen. Ich kann nicht für Lua sprechen, aber in JavaScript besteht eine der häufigsten asynchronen Aktionen darin, einen Server über AJAX zu kontaktieren.

Der AJAX-Aufruf ist asynchron, was bedeutet, dass Sie Folgendes tun:

var ajax = ajaxCall();
if(ajax == true) {
  // Do something
}

Der Inhalt des ifBlocks wird nicht zuverlässig korrekt ausgeführt. Wenn dies der Fall ist, wurde die ajaxCall()Funktion nur beendet, bevor die Ausführung die ifAnweisung erreichte.

Rückrufe beseitigen dieses Problem jedoch, indem sichergestellt wird, dass der asynchrone Aufruf beendet ist, bevor die erforderliche Funktion aufgerufen wird. Ihr Code würde sich also folgendermaßen ändern:

function ajaxCallback(response) {   
  if(response == true) {
    // Do something   
  }
}

ajaxCall(ajaxCallback);

Mit dieser Funktion können Sie beispielsweise Daten sammeln, ohne andere Dinge wie das Zeichnen der Benutzeroberfläche zu beeinträchtigen. Wenn die Datenerfassung synchron war, reagierte die Schnittstelle nicht mehr, da die Anwendung auf den Abruf der Daten wartete. Eine nicht reagierende Benutzeroberfläche ist für die Benutzererfahrung sehr schlecht, da die meisten Benutzer glauben, dass die Anwendung "abgestürzt" ist, und versuchen, den Prozess zu beenden.

Um dies in Aktion zu sehen, müssen Sie sich nur eine Website ansehen, die Aktualisierungen durchführt, ohne die gesamte Seite neu zu laden. Twitter, LinkedIn und die StackExchange-Sites sind gute Beispiele. Das "endlose Scrollen" von Feeds auf Twitter und die Benachrichtigungen von SE (sowohl für Benutzerbenachrichtigungen als auch für Benachrichtigungen, dass eine Frage neue Aktivitäten aufweist) werden durch asynchrone Aufrufe ausgelöst. Wenn Sie irgendwo ein Drehfeld sehen, bedeutet dies, dass der Abschnitt einen asynchronen Anruf getätigt hat und darauf wartet, dass dieser Anruf beendet wird. Sie können jedoch mit anderen Dingen auf der Benutzeroberfläche interagieren (und sogar andere asynchrone Anrufe tätigen). Sobald dieser Aufruf beendet ist, reagiert er und aktualisiert die Schnittstelle entsprechend.

Shauna
quelle
12

Sie haben die Frage gestellt

Warum brauchen wir "Rückruffunktionen"?

Ich werde versuchen, es kurz und klar zu beantworten (falls dies nicht gelingt, lesen Sie bitte den Wiki-Link am Ende dieser Antwort).

Callbacks dienen dazu, die konkrete Umsetzung von etwas auf den letztmöglichen Moment zu verschieben.

Das Schaltflächenbeispiel veranschaulicht dies gut. Angenommen, Sie möchten eine Schaltfläche in Ihrer Anwendung haben, die eine Seite auf dem Drucker druckt. Wir könnten uns eine Welt vorstellen, in der Sie eine ganz neue PrinterButtonKlasse codieren müssten, um dies zu tun.

Glücklicherweise haben wir den Begriff der Rückrufe (Vererbung und die Verwendung des Vorlagenmusters ist auch eine Art Rückrufsemantik), so dass wir das nicht tun müssen.

Anstatt jeden Aspekt einer Schaltfläche erneut zu implementieren, müssen wir die Implementierung der Schaltfläche erweitern. Das Buttonhat das Konzept, etwas zu tun , wenn es gedrückt wird. Wir sagen einfach, was das ist.

Dieser Mechanismus zum Einfügen von Verhalten in Frameworks wird als Rückruf bezeichnet.

Beispiel:

Lassen Sie uns ein bisschen Verhalten in eine HTML-Schaltfläche einfügen:

<button onclick="print()">Print page through a callback to the print() method</button>

onclickVerweist in diesem Fall auf einen Rückruf, der in diesem Beispiel die print()Methode ist.


http://en.wikipedia.org/wiki/Callback_(computer_programming)

AlexanderBrevig
quelle
Danke, nachdem ich Ihre Illustration gelesen habe, gehe ich zum Wiki, dessen Beispiele nur synchrone Rückrufe sind.
Lucas Li
Sagen Sie einer Funktion die Antwort jedes Mal, wenn sie ein Problem hat und Sie auf Probleme achten müssen. Lehren Sie eine Funktion, um ein Problem zu lösen, das sich von selbst erledigt.
Joe
8

Warum brauchen wir Rückruffunktionen? Auf welche Situationen kann ich dies anwenden?

Callback-Funktionen sind bei der ereignisgesteuerten Programmierung sehr nützlich. Sie ermöglichen es Ihnen, Ihr Programm so einzurichten, dass Ereignisse den richtigen Code auslösen. Dies ist sehr häufig in Programmen mit GUIs der Fall, in denen Benutzer auf ein beliebiges Element auf der Benutzeroberfläche klicken können (z. B. Schaltflächen oder Menüelemente) und der entsprechende Code ausgeführt wird.

FrustratedWithFormsDesigner
quelle
1
+ Dafür sind Rückrufe gut, aber ich hasse es, sie schreiben zu müssen :-)
Mike Dunlavey
Als ich noch ein Neuling war, hat uns mein Lehrer das Programmieren in MFC beigebracht, aber er hat uns nie gesagt, was Rückruf ist :-(
Lucas Li
2

Was passiert ohne Rückruf:

Guy 1: Okay, ich warte darauf, dass das passiert. (Pfeifen, Daumen drehen)

Typ 2: Verdammt! Warum zeigt mir Guy 1 nichts? Ich möchte sehen, was passiert !! Wo ist mein Zeug? Wie soll irgendjemand hier irgendetwas erledigen?

Jim
quelle
1

Zunächst einmal bezieht sich der Begriff Rückruf auf einen Abschluss, der für etwas verwendet wird.

Angenommen, Sie erstellen eine Abschlussfunktion und speichern sie einfach in einer Variablen. Es ist kein Rückruf, weil es für nichts verwendet wird.

Angenommen, Sie erstellen einen Abschluss und speichern ihn an einem Ort, an dem er aufgerufen wird, wenn etwas passiert. Es wird jetzt als Rückruf bezeichnet.

Normalerweise werden Rückrufe von anderen Programmteilen als denjenigen erstellt, die sie aufrufen. Man kann sich also leicht vorstellen, dass einige Teile des Programms die anderen Teile "zurückrufen".

Einfach ausgedrückt, erlauben Rückrufe einem Teil des Programms, einem anderen Teil anzuweisen, etwas (irgendetwas) zu tun, wenn etwas passiert.

Was die Variablen betrifft, die aufgrund der Verwendung in einem Closure am Leben bleiben, so ist dies eine völlig andere Funktion, die als Upvalues ​​bezeichnet wird (auch bekannt als "Verlängerung der Lebensdauer lokaler Variablen" bei Personen, die kein Lua sprechen). Und es ist auch ziemlich nützlich.

Jonathan Graef
quelle
ja, Extending the lifetime of local variablesist auch der begriff zu sagen upvalue.
Lucas Li
Hmm, interessant. Der Begriff " Wertsteigerung" scheint für Lua spezifisch zu sein, obwohl eine schnelle Google-Suche zeigt, dass er gelegentlich zur Beschreibung anderer Sprachen verwendet wurde. Ich werde es zu meiner Antwort hinzufügen.
Jonathan Graef
1

Rückrufe gibt es zumindest seit den Anfängen von Fortran. Wenn Sie beispielsweise einen ODE-Löser (gewöhnlicher Differentialgleichungslöser) haben, z. B. einen Runge-Kutta-Löser, könnte dies folgendermaßen aussehen:

subroutine rungekutta(neq, t, tend, y, deriv)
  integer neq             ! number of differential equations
  double precision t      ! initial time
  double precision tend   ! final time
  double precision y(neq) ! state vector
  ! deriv is the callback function to evaluate the state derivative
  ...
  deriv(neq, t, y, yprime) ! call deriv to get the state derivative
  ...
  deriv(neq, t, y, yprime) ! call it several times
  ...
end subroutine

Es ermöglicht dem Aufrufer, das Verhalten der Unterroutine anzupassen, indem ihm eine Sonderfunktion übergeben wird, die bei Bedarf aufgerufen werden kann. Auf diese Weise kann der ODE-Solver zur Simulation einer Vielzahl von Systemen verwendet werden.

Mike Dunlavey
quelle
1

Ich bin darauf gestoßen und wollte eine aktuellere Antwort geben ...

Rückruffunktionen ermöglichen es uns, zu einem späteren Zeitpunkt etwas mit Daten zu tun , sodass der Rest unseres Codes ausgeführt werden kann, anstatt darauf zu warten. Der asynchrone Code ermöglicht uns den Luxus, etwas später auszuführen . Das am besten lesbare Beispiel für Rückruffunktionen, auf die ich gestoßen bin, sind Versprechen in JavaScript. Im folgenden Beispiel sind dies immer dann Rückruffunktionen, wenn Sie die Funktion (result) oder (newResult) oder (finalResult) sehen . Der Code in geschweiften Klammern wird ausgeführt, sobald die Daten vom Server zurückkommen. Nur zu diesem Zeitpunkt wäre es sinnvoll, diese Funktionen auszuführen, da jetzt die Daten zur Verfügung stehen, die sie benötigen.

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

Der Code stammt aus ... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

Hoffentlich hilft das jemandem. Dies hat mir geholfen, Rückrufe zu verstehen :)

NRV
quelle
1
Dies scheint nichts Wesentliches zu bieten über Punkte, die in den vorherigen 9 Antworten gemacht und erklärt wurden
Mücke
1
Vielleicht für Sie, aber das hat mir wirklich die Rückrufe verdeutlicht, und ich dachte, ich würde mit Ihnen teilen. IMO ist die Lesbarkeit des Beispiels, was es lohnt. Ich bin mir sicher, dass wir uns darauf einigen können, dass die Lesbarkeit von Code einen großen Beitrag leistet, insbesondere für Anfänger.
NRV
-2

Ich kenne lua nicht, aber im Allgemeinen Rückrufmethoden wie Multithreading. Zum Beispiel funktionieren die meisten Anwendungen in der Entwicklung mobiler Anwendungen so, als ob sie eine Anfrage an den Server senden und mit der Benutzeroberfläche spielen, wobei die Daten als Antwort vom Server kommen. Wenn der Benutzer eine Anfrage an den Server sendet, dauert es einige Zeit, bis die Antwort vom Server eingeht, aber die beste UX-Benutzeroberfläche sollte nicht hängen bleiben.

Aus diesem Grund verwenden wir mehrere Threads, um parallele Operationen durchzuführen. Wenn wir die Antwort vom Server erhalten, müssen wir die Benutzeroberfläche aktualisieren. Wir müssen von diesem Thread benachrichtigen, um zu aktualisieren. aus der funktionalen Welt. Diese Art von Funktionsaufrufen wird als Rückrufmethode bezeichnet. Wenn Sie diese Methoden aufrufen, sollte die Steuerung zum Hauptthread zurückkehren. Rückrufmethoden sind beispielsweise Blöcke in Objective-C.

AMohan
quelle
Ja, was Sie gesagt haben, entspricht meinem Verständnis nach dem Lesen des Buches.
Lucas Li
2
Callback-Methoden sind nichts anderes als Multithreading. Callback-Methoden sind einfach Funktionszeiger (Delegates). In Threads geht es um das Umschalten / Verwenden des CPU-Kontexts. Ein stark vereinfachter Gegensatz wäre, dass es beim Threading um die CPU-Optimierung für UX geht, während es bei Rückrufen um das Umschalten des Speichers für den Polymorphismus geht. Nur weil Threads möglicherweise Rückrufe ausführen, bedeutet dies nicht, dass Threading und Rückrufe ähnlich sind. Threads führen auch statische Methoden für statische Klassen aus, aber Threading und statische Typen sind nicht ähnlich.
Risma