Co to jest funkcja oddzwaniania?


684

Co to jest funkcja oddzwaniania?


8
można znaleźć tutaj: stackoverflow.com/a/9652434/3343174 najlepsze wyjaśnienie dotyczące wywołań zwrotnych
Fakher


Spójrz na drugą odpowiedź, aby uzyskać szczegółowe wyjaśnienie
Donato,

Najlepsze wytłumaczenie oddzwonienia, jakie kiedykolwiek znalazłem youtube.com/watch?v=xHneyv38Jro
Sameer Sinha

Odpowiedzi:


681

Deweloperzy często są zdezorientowani tym, czym jest oddzwanianie z powodu nazwy cholernej rzeczy.

Funkcja oddzwaniania to funkcja, która jest:

  • dostępne przez inną funkcję, oraz
  • jest wywoływany po pierwszej funkcji, jeśli ta pierwsza funkcja zakończy się

Dobrym sposobem wyobrażenia sobie, jak działa funkcja wywołania zwrotnego jest to, że jest to funkcja, która jest „ wywoływana z tyłu” ” funkcji, do której jest przekazywana.

Może lepszą nazwą byłoby „połączenie po” funkcja .

Ta konstrukcja jest bardzo przydatna w przypadku zachowania asynchronicznego, w którym chcemy, aby działanie miało miejsce po zakończeniu poprzedniego zdarzenia.

Pseudo kod:

// A function which accepts another function as an argument
// (and will automatically invoke that function when it completes - note that there is no explicit call to callbackFunction)
funct printANumber(int number, funct callbackFunction) {
    printout("The number you provided is: " + number);
}

// a function which we will use in a driver function as a callback function
funct printFinishMessage() {
    printout("I have finished printing numbers.");
}

// Driver method
funct event() {
   printANumber(6, printFinishMessage);
}

Wynik, jeśli wywołałeś event ():

The number you provided is: 6
I have finished printing numbers.

Ważna jest tutaj kolejność danych wyjściowych. Ponieważ funkcje wywołania zwrotnego są wywoływane później, „Skończyłem drukować numery” jest drukowane jako ostatnie, a nie pierwsze.

Oddzwonienia są nazywane ze względu na ich użycie w językach wskaźników. Jeśli nie używasz żadnego z nich, nie pracuj nad nazwą „callback”. Po prostu zrozum, że jest to tylko nazwa opisująca metodę podaną jako argument do innej metody, na przykład, gdy wywoływana jest metoda nadrzędna (niezależnie od warunków, takich jak kliknięcie przycisku, znacznik czasowy itp.), A jej treść kończy się, następnie wywoływana jest funkcja zwrotna.

Niektóre języki obsługują konstrukcje, w których obsługiwanych jest wiele argumentów funkcji wywołania zwrotnego, i są wywoływane na podstawie sposobu wykonania funkcji nadrzędnej (tj. Jedno wywołanie zwrotne jest wywoływane w przypadku pomyślnego zakończenia funkcji nadrzędnej, a inne w przypadku, gdy funkcja nadrzędna generuje konkretny błąd itp.).


31
Twój przykład jest świetny, ale nie rozumiem, dlaczego terminologia to „callback”. Kiedy znaczy „oddzwonić” w znaczeniu znaczącym?
CodyBugstein

4
Cześć, około once its parent method completes, the function which this argument represents is then called. Więc jeśli funkcja jest przekazywana do innej funkcji jako argument, ale wywoływana ze środka środowiska uruchomieniowego funkcji nadrzędnej, tak jak parent(cb) {dostuff1(); cb(); dostuff2()}wtedy, nie jest uważana za callbackfunkcję?
Max Yari,

2
@MaxYari: IMHO, nadal jest uważane za oddzwanianie. Ważną rzeczą jest to, że funkcja nadrzędna będzie w jakiś sposób korzystać z funkcji wprowadzania (aka oddzwaniania). Można go wywołać w środku lub na końcu, lub jeśli warunek jest spełniony.
Kamran Bigdely,

12
@ 8bitjunkie dziękuję - ale gdzie jest wywoływana metoda znaczącaOfLife w funkcji printANumber?
BKSpurgeon

2
To wcale nie jest prawda: „jest automatycznie wywoływane po zakończeniu tej pierwszej funkcji”. Oddzwonienie wcale nie musi być wykonywane, a tym bardziej automatycznie. W rzeczywistości często zdarza się, że wywołania zwrotne są wykonywane przed zakończeniem działania funkcji nadrzędnej. Bardzo nie lubię, jak ludzie opisują wywołania zwrotne jako funkcje, które są wykonywane „później”. Jest to bardzo mylące dla osób, które się o nich uczą. Mówiąc najprościej, wywołania zwrotne to tylko funkcje przekazywane jako argumenty do innych funkcji. Kropka. Lepsze wyjaśnienie obejmowałoby wyjaśnienie DLACZEGO wywołania zwrotne zamiast odwołań do funkcji.
Jordan

225

Nieprzezroczysta definicja

Funkcja oddzwaniania to funkcja udostępniana w innym fragmencie kodu, umożliwiająca wywołanie go przez ten kod.

Wymyślony przykład

Dlaczego chcesz to zrobić? Powiedzmy, że istnieje usługa, którą musisz wywołać. Jeśli usługa natychmiast powróci, po prostu:

  1. Nazwać
  2. Poczekaj na wynik
  3. Kontynuuj, gdy pojawi się wynik

Załóżmy na przykład, że usługa była factorialfunkcją. Gdy chcesz tę wartość 5!, możesz wywołać factorial(5), a następują następujące kroki:

  1. Bieżąca lokalizacja wykonania jest zapisywana (na stosie, ale to nie jest ważne)

  2. Egzekucja jest przekazywana do factorial

  3. Po factorialzakończeniu umieszcza wynik w miejscu, w którym można się do niego dostać

  4. Egzekucja wraca do miejsca, w którym była [1]

Załóżmy teraz, że factorialzajęło to naprawdę dużo czasu, ponieważ podajesz jej ogromne liczby i musi on działać gdzieś w jakimś klastrze superkomputerów. Załóżmy, że oczekujesz 5 minut na zwrócenie wyniku. Mógłbyś:

  1. Zachowaj swój projekt i uruchom program w nocy, gdy śpisz, abyś nie patrzył na ekran przez pół czasu

  2. Zaprojektuj swój program do robienia innych rzeczy, podczas gdy factorialon robi swoje

Jeśli wybierzesz drugą opcję, oddzwanianie może działać.

Kompleksowy projekt

Aby wykorzystać wzorzec wywołania zwrotnego, chcesz móc wywoływać factorialw następujący sposób:

factorial(really_big_number, what_to_do_with_the_result)

Drugi parametr, what_to_do_with_the_resultto funkcja, do której wysyłasz factorial, w nadziei, że factorialwywoła go na podstawie wyniku przed powrotem.

Tak, oznacza to, że factorialnależy napisać, aby obsługiwać połączenia zwrotne.

Załóżmy teraz, że chcesz przekazać parametr do wywołania zwrotnego. Teraz nie możesz, bo nie będziesz tak nazywać, factorialjest. Trzeba więc factorialnapisać, aby umożliwić przekazanie parametrów, a on po prostu przekaże je do oddzwaniania, gdy je wywoła. Może to wyglądać tak:

factorial (number, callback, params)
{
    result = number!   // i can make up operators in my pseudocode
    callback (result, params)
}

Teraz, gdy factorialpozwala na to wzorzec, Twoje wywołanie zwrotne może wyglądać następująco:

logIt (number, logger)
{
    logger.log(number)
}

i twoje wezwanie factorialbyłoby

factorial(42, logIt, logger)

Co jeśli chcesz coś zwrócić logIt? Nie możesz, bo factorialnie zwracasz na to uwagi.

Dlaczego nie możesz factorialpo prostu zwrócić tego, co zwraca Twoje połączenie zwrotne?

Dzięki temu nie blokuje

Ponieważ wykonanie ma zostać przekazane do oddzwaniania, gdy factorialjest zakończone, tak naprawdę nie powinno ono zwracać niczego swojemu rozmówcy. Idealnie byłoby, gdyby w jakiś sposób uruchomił swoją pracę w innym wątku / procesie / maszynie i natychmiast powrócił, abyś mógł kontynuować, być może coś takiego:

factorial(param_1, param_2, ...)
{
    new factorial_worker_task(param_1, param_2, ...);
    return;
}

Jest to teraz „wywołanie asynchroniczne”, co oznacza, że ​​po wywołaniu natychmiast wraca, ale tak naprawdę jeszcze nie wykonał swojej pracy. Potrzebujesz więc mechanizmów, aby to sprawdzić i uzyskać wynik po zakończeniu, a Twój program stał się bardziej skomplikowany.

Nawiasem mówiąc, przy użyciu tego wzorca factorial_worker_taskmożna asynchronicznie uruchomić oddzwonienie i natychmiast wrócić.

Więc co robisz?

Odpowiedzią jest pozostanie w ramach wzorca oddzwaniania. Kiedykolwiek chcesz pisać

a = f()
g(a)

i fma być wywoływany asynchronicznie, zamiast tego napiszesz

f(g)

gdzie gjest przekazywany jako oddzwonienie.

To zasadniczo zmienia topologię przepływu twojego programu i wymaga trochę czasu, aby się przyzwyczaić.

Twój język programowania może ci bardzo pomóc, umożliwiając tworzenie funkcji w locie. W powyższym kodzie funkcja gmoże być tak mała jak print (2*a+1). Jeśli twój język wymaga zdefiniowania go jako osobnej funkcji z całkowicie niepotrzebną nazwą i podpisem, wówczas twoje życie stanie się nieprzyjemne, jeśli będziesz często używać tego wzorca.

Jeśli natomiast Twój język pozwala ci tworzyć lambdy, to jesteś w znacznie lepszej formie. Potem skończysz pisać coś w stylu

f( func(a) { print(2*a+1); })

co jest o wiele ładniejsze.

Jak przekazać połączenie zwrotne

Jak przekazałbyś funkcję oddzwaniania factorial? Cóż, możesz to zrobić na wiele sposobów.

  1. Jeśli wywoływana funkcja działa w tym samym procesie, można przekazać wskaźnik funkcji

  2. A może chcesz zachować słownik fn name --> fn ptrw swoim programie, w którym to przypadku możesz podać nazwę

  3. Być może twój język pozwala zdefiniować funkcję w miejscu, możliwą jako lambda! Wewnętrznie tworzy jakiś obiekt i przekazuje wskaźnik, ale nie musisz się tym martwić.

  4. Być może funkcja, którą wywołujesz, działa na całkowicie oddzielnym komputerze i wywołujesz ją za pomocą protokołu sieciowego, takiego jak HTTP. Możesz ujawnić swoje wywołanie zwrotne jako funkcję wywoływaną przez HTTP i przekazać jego adres URL.

Masz pomysł.

Ostatni wzrost oddzwaniania

W erze internetowej, w którą weszliśmy, usługi, które wywołujemy, są często realizowane przez sieć. Często nie mamy żadnej kontroli nad tymi usługami, tj. Nie napisaliśmy ich, nie utrzymujemy ich, nie możemy upewnić się, że działają lub nie działają.

Ale nie możemy oczekiwać, że nasze programy się zablokują, gdy czekamy na odpowiedź tych usług. Mając to na uwadze, usługodawcy często projektują interfejsy API przy użyciu wzorca wywołania zwrotnego.

JavaScript bardzo ładnie obsługuje połączenia zwrotne, np. Z lambdami i zamknięciami. W świecie JavaScript istnieje duża aktywność, zarówno w przeglądarce, jak i na serwerze. Istnieją nawet platformy JavaScript opracowywane dla urządzeń mobilnych.

W miarę postępów coraz więcej z nas będzie pisać kod asynchroniczny, dla którego to zrozumienie będzie niezbędne.


1
Koncepcja bardzo dobrze wyjaśniona ..! :)
piyushGoyal

Świetne wyjaśnienie, +1.
Lingamurthy CS

1
To najlepsza odpowiedź spośród wszystkich innych. Uprzejmie głosujcie.
Abhishek Nalin

3
Doskonale wyjaśnione i wszystko wyjaśnione. Chciałbym móc ponownie głosować.
Yogesh Yadav

Tak, rozumiem, jak lambas działają w javascript i ruby. I java 8, ale stare wersje java nie używały lambas, a zamiast tego używały klas i chciałbym wiedzieć, jak działa ten rodzaj wywołania zwrotnego. Nadal najlepsza odpowiedź na drugą.
Donato,

97

Pamiętaj, że oddzwonienie to jedno słowo.

Strona zwrotna Wikipedii bardzo dobrze to wyjaśnia.

cytat ze strony wikipedii:

W programowaniu komputerowym wywołanie zwrotne to odwołanie do kodu wykonywalnego lub fragmentu kodu wykonywalnego, który jest przekazywany jako argument do innego kodu. Dzięki temu warstwa oprogramowania niższego poziomu może wywoływać podprogram (lub funkcję) zdefiniowany w warstwie wyższego poziomu.


14
Miły sposób na przedstawienie odpowiedzi.
Chathuranga Chandrasekara

1
A to prowadzi również do odpowiedzi w inny sposób. Rzeczownik „callback” to „callback”, podobnie jak coś, co przeszło zamknięcie, zostało zamknięte, a czymś, co służy do logowania, jest login.
Anonimowy

22
To mógłby być komentarz - w zasadzie jest to link do Wikipedii
CodyBugstein

Wikipedia ma naprawdę niesamowite rzeczy do programowania. Zawsze czułem, że termin „oddzwanianie” najlepiej tłumaczyć za pomocą wyrażenia „
Thomas

Świetne wyjaśnienie na javascriptissexy.com/…; które tutaj ponownie opublikuję; Funkcja zwrotna to funkcja przekazywana do innej funkcji jako parametr, a funkcja zwrotna jest wywoływana lub wykonywana wewnątrz drugiej funkcji. // Zauważ, że element w parametrze metody click jest funkcją, a nie zmienną. // Element jest funkcją zwrotną $ („# btn_1”). Click (function () {alert („Btn 1 Clicked”) );}); Jak widać w poprzednim przykładzie, przekazujemy funkcję jako parametr do metody click, aby mogła zostać wykonana -
MarcoZen

46

Laik odpowiedziałby, że jest to funkcja, która nie jest wywoływana przez ciebie, ale przez użytkownika lub przeglądarkę po wystąpieniu określonego zdarzenia lub po przetworzeniu jakiegoś kodu.


42

Funkcja zwrotna to taka, którą należy wywołać, gdy spełniony zostanie określony warunek. Zamiast wywoływania natychmiastowego funkcja wywołania zwrotnego jest wywoływana w pewnym momencie w przyszłości.

Zwykle jest używany, gdy uruchamiane jest zadanie, które zakończy się asynchronicznie (tj. Zakończy się jakiś czas po powrocie funkcji wywołującej).

Na przykład funkcja żądania strony internetowej może wymagać od jej osoby wywołującej funkcji wywołania zwrotnego, która zostanie wywołana po zakończeniu pobierania strony internetowej.


W pierwszym zdaniu mówisz, "...when a condition is met"ale myślałem, że wywołania zwrotne są wywoływane, gdy funkcja nadrzędna kończy wykonywanie i nie są zależne od warunków (?).
Ojonugwa Jude Ochalifu

„Pewny warunek” oznacza po prostu, że generalnie są oni wywoływani z jakiegoś powodu, a nie losowo. Odwołanie może być wywołane, gdy rodzic / twórca nadal wykonuje - może to prowadzić do wyścigu, jeśli programista go nie spodziewa.
Thomas Bratt

W porządku, dziękuję za wyjaśnienie
Ojonugwa Jude Ochalifu

34

Połączenia zwrotne najłatwiej opisać w kategoriach systemu telefonicznego. Wywołanie funkcji jest analogiczne do dzwonienia do kogoś przez telefon, zadawania jej pytań, uzyskiwania odpowiedzi i rozłączania się; dodanie oddzwaniania zmienia analogię, więc po zadaniu jej pytania podajesz jej także swoje imię i numer, aby mogła oddzwonić z odpowiedzią.

- Paul Jakubik, „Implementacje wywołania zwrotnego w C ++”


1
Więc moje imię i numer są funkcją?
Koray Tugay

Nie, to byłaby analogia, gdyby „oddzwonienie” było dobrym imieniem tego, co jest zamiast tego: prosisz operatora telefonicznego o wykonanie połączenia. Koniec.
gherson

33

Uważam, że ten żargon „oddzwaniania” został błędnie użyty w wielu miejscach. Moja definicja będzie taka:

Funkcja oddzwaniania to funkcja przekazywana komuś i umożliwiająca mu wywołanie go w pewnym momencie.

Myślę, że ludzie po prostu czytają pierwsze zdanie definicji wiki:

wywołanie zwrotne to odwołanie do kodu wykonywalnego lub fragmentu kodu wykonywalnego, który jest przekazywany jako argument do innego kodu.

Pracowałem z wieloma interfejsami API, zobacz różne złe przykłady. Wiele osób nazywa wskaźnik funkcji (odniesienie do kodu wykonywalnego) lub funkcje anonimowe (część kodu wykonywalnego) „callback”, jeśli są to tylko funkcje, dlaczego potrzebujesz do tego innej nazwy?

Właściwie tylko drugie zdanie w definicji wiki pokazuje różnice między funkcją zwrotną a funkcją normalną:

Dzięki temu warstwa oprogramowania niższego poziomu może wywoływać podprogram (lub funkcję) zdefiniowany w warstwie wyższego poziomu.

więc różnica polega na tym, kto przekaże funkcję i jak zostanie wywołana funkcja przekazana. Jeśli po prostu zdefiniujesz funkcję i przekażesz ją innej funkcji i wywołasz ją bezpośrednio w treści funkcji, nie nazywaj go wywołaniem zwrotnym. Definicja mówi, że przekazana funkcja zostanie wywołana przez funkcję „niższego poziomu”.

Mam nadzieję, że ludzie przestaną używać tego słowa w dwuznacznym kontekście, nie pomoże to lepiej zrozumieć, tylko gorzej.


2
Twoja odpowiedź ma sens ... ale mam problem z jej wyobrażeniem. Czy możesz podać przykład?
CodyBugstein

3
@Zane Wong :: W ostatnim napisałeś: „Definicja mówi, że twoja przekazana funkcja zostanie wywołana przez funkcję„ niższego poziomu ”. Czy możesz wyjaśnić, co oznacza funkcja niższego poziomu? Lepiej, jeśli podasz przykład.
Viku

Przykład byłby miły
Yousuf Azad

1
Myślę, że różnica między klasycznym wywołaniem funkcji a stylem wywołania zwrotnego polega na powiązaniu z kierunkiem zależności: jeśli moduł A zależy od („używa”) modułu B, A wywołuje funkcję B, nie jest to wywołanie zwrotne. Jeśli A przekazuje odwołanie do swojej funkcji do B, wówczas B wywołuje funkcję A, jest to wywołanie zwrotne: wywołanie jest cofane w porównaniu do zależności modułu.
XouDo,

30

Uprośćmy to. Co to jest funkcja oddzwaniania?

Przykład według przypowieści i analogii

Mam sekretarkę Każdego dnia proszę ją: (i) zostaw pocztę wychodzącą firmy na poczcie, a po tym, aby to zrobiła: (ii) cokolwiek zadanie, które napisałem dla niej na jednej z tych karteczek .

Jakie jest teraz zadanie na karteczce? Zadanie różni się z dnia na dzień.

Załóżmy, że w tym konkretnym dniu wymagam od niej wydrukowania niektórych dokumentów. Zapisuję to na karteczce i przypinam ją do jej biurka wraz z pocztą wychodzącą, którą musi wysłać.

W podsumowaniu:

  1. po pierwsze, musi wysyłać pocztę i
  2. zaraz po tym musi wydrukować niektóre dokumenty.

Funkcja oddzwaniania to drugie zadanie: wydruk tych dokumentów. Ponieważ odbywa się to PO Zesłaniu poczty, a także dlatego, że wraz z pocztą, którą musi wysłać, zostaje jej przekazana karteczka samoprzylepna z informacją o wydrukowaniu dokumentu.

Połączmy to teraz ze słownictwem programistycznym

  • Nazwa metody w tym przypadku to: DropOffMail.
  • A funkcja oddzwaniania to: PrintOffDocuments. PrintOffDocuments to funkcja oddzwaniania, ponieważ chcemy, aby sekretarka to zrobiła, dopiero po uruchomieniu DropOffMail.
  • Chciałbym więc „przekazać: PrintOffDocuments jako„ argument ”do metody DropOffMail. Jest to ważna kwestia.

To wszystko. Nic więcej. Mam nadzieję, że to ci wyjaśniło - a jeśli nie, opublikuj komentarz, a ja postaram się wyjaśnić.


18

To sprawia, że ​​wywołania zwrotne brzmią jak instrukcje zwrotne na końcu metod.

Nie jestem pewien, czy takie są.

Myślę, że wywołania zwrotne są w rzeczywistości wywołaniem funkcji, jako konsekwencja wywołania i zakończenia innej funkcji.

Myślę też, że oddzwanianie ma na celu adresowanie wywołania, w rodzaju „hej! Tego, o co prosiłeś? Zrobiłem to - pomyślałem, że dam ci znać - z powrotem do ciebie”.


1
+1 za przesłuchanie zwrotne i zwrotne. Byłem przyłapany na tym, podobnie jak wielu absolwentów, z którymi pracuję.
8bitjunkie

2
Dobra odpowiedź - pomogła mi ją zrozumieć w przeciwieństwie do wielu innych odpowiedzi!
adaam

18

Co to jest oddzwanianie ?

  • Zasadniczo połączenie telefoniczne wykonane w celu zwrócenia tego, które ktoś otrzymał.
  • W obliczeniach wywołanie zwrotne jest fragmentem kodu wykonywalnego, który jest przekazywany jako argument do innego kodu. Kiedy funkcja jest wykonywana z działaniem (lub gdy wystąpiło jakieś zdarzenie), wywołuje funkcję oddzwaniania (oddzwania - stąd nazwa).

Co to jest funkcja oddzwaniania ?

  • funkcja oddzwaniania jest jak Sługa, który „oddzwania” do swego Mistrza, kiedy wykonał zadanie.
  • funkcja zwrotna jest funkcją, która jest przekazywana do innej funkcji (nazwijmy to innej funkcji otherFunction) jako parametr, a funkcja zwrotna jest wywoływana (lub wykonywane) wewnątrz otherFunction.
    function action(x, y, callback) {
        return callback(x, y);
    }

    function multiplication(x, y) {
        return x * y;
    }

    function addition(x, y) {
        return x + y;
    }

    alert(action(10, 10, multiplication)); // output: 100

    alert(action(10, 10, addition)); // output: 20

W SOA funkcja zwrotna umożliwia modułom wtyczek dostęp do usług z kontenera / środowiska.

Analogia: oddzwanianie. Asynchroniczny. Nieblokujący
Przykład z życia dla oddzwaniania


Funkcja oddzwaniania sama w sobie nie jest funkcją wyższego rzędu. Jest przekazywany do funkcji wyższego rzędu.
Danio

17

Call After byłoby lepszą nazwą niż głupia nazwa, callback . Kiedy lub jeśli warunek zostanie spełniony w funkcji, wywołaj inną funkcję, funkcję Call After , tę otrzymaną jako argument.

Zamiast na stałe zakodować wewnętrzną funkcję w funkcji, pisze się funkcję, aby zaakceptować już zapisaną funkcję Call After jako argument. Funkcja Call After może zostać wywołana na podstawie zmian stanu wykrytych przez kod w funkcji odbierającej argument.


To świetny pomysł. Wybrałem „wezwany z tyłu”, aby spróbować to wyjaśnić. Widziałem kogoś takiego jak Martin Fowler popularyzujący „call after” jako nowy termin na ich blogu.
8bitjunkie

15

Funkcja wywołania zwrotnego to funkcja określona przez użytkownika do istniejącej funkcji / metody, która ma być wywoływana po zakończeniu akcji, wymaga dodatkowego przetwarzania itp.

Na przykład w JavaScript, a dokładniej jQuery, możesz określić argument wywołania zwrotnego, który ma być wywoływany po zakończeniu animacji.

W PHP preg_replace_callback()funkcja ta umożliwia udostępnienie funkcji, która będzie wywoływana po dopasowaniu wyrażenia regularnego, z przekazaniem ciągów dopasowanych jako argumenty.


10

spójrz na obrazek :)tak to działa

Główny program wywołuje funkcję biblioteki (która może być również funkcją na poziomie systemu) z nazwą funkcji zwrotnej. Ta funkcja zwrotna może być zaimplementowana na wiele sposobów. Główny program wybiera jedno wywołanie zwrotne zgodnie z wymaganiami.

Wreszcie funkcja biblioteki wywołuje funkcję wywołania zwrotnego podczas wykonywania.


7
Czy mógłbyś również dodać do tego wyjaśnienie tekstowe ? Jeśli obraz znika, ta odpowiedź traci cały kontekst.
Tim Post

tekst od innych ludzi wyjaśnia to najlepiej. brakuje mi tylko obrazu :)

Ze wszystkich długich opisów, które tu widziałem, ten jest tym, który zmusił mnie do bycia „ahhhhh, teraz widzę jego zastosowanie”. Zyskaj głos.
DiBosco

7

Prosta odpowiedź na to pytanie jest taka, że ​​funkcja zwrotna to funkcja wywoływana za pomocą wskaźnika funkcji. Jeśli przekażesz wskaźnik (adres) funkcji jako argument do innej, gdy ten wskaźnik jest używany do wywoływania funkcji, wskazuje na to, że jest wywoływane oddzwanianie


7

Załóżmy, że mamy funkcję, w sort(int *arraytobesorted,void (*algorithmchosen)(void))której może zaakceptować wskaźnik funkcji jako argument, którego można użyć w pewnym momencie sort()implementacji. Następnie tutaj kod adresowany przez wskaźnik funkcji algorithmchosenjest wywoływany jako funkcja zwrotna .

Zaletą jest to, że możemy wybrać dowolny algorytm, taki jak:

  1.    algorithmchosen = bubblesort
  2.    algorithmchosen = heapsort
  3.    algorithmchosen = mergesort   ...

Które zostały, powiedzmy, zaimplementowane z prototypem:

  1.   `void bubblesort(void)`
  2.   `void heapsort(void)`
  3.   `void mergesort(void)`   ...

Jest to koncepcja stosowana do osiągnięcia polimorfizmu w programowaniu obiektowym


Świetne wyjaśnienie na javascriptissexy.com/…; które tutaj ponownie opublikuję; Funkcja zwrotna to funkcja przekazywana do innej funkcji jako parametr, a funkcja zwrotna jest wywoływana lub wykonywana wewnątrz drugiej funkcji. // Zauważ, że element w parametrze metody click jest funkcją, a nie zmienną. // Element jest funkcją zwrotną $ („# btn_1”). Click (function () {alert („Btn 1 Clicked”) );}); Jak widać w poprzednim przykładzie, przekazujemy funkcję jako parametr do metody click, aby mogła zostać wykonana -
MarcoZen

4

„W programowaniu komputerowym wywołanie zwrotne to odniesienie do kodu wykonywalnego lub fragmentu kodu wykonywalnego, który jest przekazywany jako argument do innego kodu. Dzięki temu warstwa oprogramowania niższego poziomu może wywoływać podprogram (lub funkcję) zdefiniowany w warstwie wyższego poziomu. ” - Wikipedia

Oddzwanianie w C za pomocą wskaźnika funkcji

W C wywołanie zwrotne jest realizowane za pomocą wskaźnika funkcji. Wskaźnik funkcji - jak sama nazwa wskazuje, jest wskaźnikiem funkcji.

Na przykład int (* ptrFunc) ();

W tym przypadku ptrFunc jest wskaźnikiem do funkcji, która nie przyjmuje argumentów i zwraca liczbę całkowitą. NIE zapomnij umieścić w nawiasie, w przeciwnym razie kompilator przyjmie, że ptrFunc jest normalną nazwą funkcji, która nie bierze niczego i zwraca wskaźnik do liczby całkowitej.

Oto kod ilustrujący wskaźnik funkcji.

#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;
}

Teraz spróbujmy zrozumieć pojęcie wywołania zwrotnego w C za pomocą wskaźnika funkcji.

Cały program ma trzy pliki: callback.c, reg_callback.h i 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)();                               
}

Jeśli uruchomimy ten program, wynikiem będzie

Jest to program demonstrujący wywołanie zwrotne funkcji wewnątrz register_callback wewnątrz my_callback z powrotem w programie głównym

Funkcja wyższej warstwy wywołuje funkcję dolnej warstwy jako normalne wywołanie, a mechanizm zwrotny pozwala funkcji dolnej warstwy na wywołanie funkcji wyższej warstwy przez wskaźnik do funkcji wywołania zwrotnego.

Oddzwanianie w Javie za pomocą interfejsu

Java nie ma pojęcia wskaźnika funkcji. Implementuje mechanizm wywołania zwrotnego za pośrednictwem mechanizmu interfejsu. Zamiast wskaźnika funkcji deklarujemy interfejs posiadający metodę, która zostanie wywołana, gdy odbiorca zakończy swoje zadanie

Pokażę to na przykładzie:

Interfejs oddzwaniania

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

Wzywający lub klasa wyższego poziomu

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
}
}

Funkcja Callee lub dolnej warstwy

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
}
}

Oddzwanianie za pomocą wzorca EventListener

  • Element listy

Ten wzorzec służy do powiadamiania od 0 do n liczby obserwatorów / słuchaczy, że dane zadanie zostało zakończone

  • Element listy

Różnica między mechanizmem wywoływania zwrotnego a mechanizmem EventListener / Observer polega na tym, że w wywołaniu zwrotnym odbiorca powiadamia pojedynczego rozmówcę, podczas gdy w Eventlisener / Observer odbiorca może powiadomić każdego, kto jest zainteresowany tym zdarzeniem (powiadomienie może przejść do niektórych innych części aplikacja, która nie uruchomiła zadania)

Pozwól, że wyjaśnię to na przykładzie.

Interfejs zdarzeń

public interface Events {

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

Widżet klasy

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);
    }
}

Przycisk klasy

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

Pole wyboru klasy

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;
}
}

Klasa aktywności

pakiet 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();
    }   
}

Inna klasa

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...");
}
}

Główna klasa

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);
}
}

Jak widać z powyższego kodu, mamy interfejs zwany zdarzeniami, który zasadniczo zawiera listę wszystkich zdarzeń, które mogą wystąpić w naszej aplikacji. Klasa Widget jest klasą bazową dla wszystkich składników interfejsu użytkownika, takich jak Button, Checkbox. Te składniki interfejsu użytkownika są obiektami, które faktycznie odbierają zdarzenia z kodu frameworka. Klasa widżetów implementuje interfejs zdarzeń, a także ma dwa zagnieżdżone interfejsy, a mianowicie OnClickEventListener i OnLongClickEventListener

Te dwa interfejsy są odpowiedzialne za nasłuchiwanie zdarzeń, które mogą wystąpić w komponentach interfejsu użytkownika pochodzących z widgetów, takich jak Button lub Checkbox. Jeśli więc porównamy ten przykład z wcześniejszym przykładem wywołania zwrotnego przy użyciu interfejsu Java, te dwa interfejsy działają jako interfejs wywołania zwrotnego. Zatem kod wyższego poziomu (działanie tutaj) implementuje te dwa interfejsy. I ilekroć wystąpi zdarzenie w widgecie, wywoływany będzie kod wyższego poziomu (lub metoda tych interfejsów zaimplementowana w kodzie wyższego poziomu, czyli tutaj Działanie).

Teraz pozwól mi omówić podstawową różnicę między wzorcem wywołania zwrotnego a listą zdarzeń. Jak już wspomnieliśmy, za pomocą funkcji oddzwaniania Callee może powiadomić tylko jednego rozmówcę. Ale w przypadku wzorca EventListener każda inna część lub klasa aplikacji może zarejestrować zdarzenia, które mogą wystąpić w przycisku lub polu wyboru. Przykładem tego rodzaju klasy jest OtherClass. Jeśli zobaczysz kod OtherClass, przekonasz się, że zarejestrował się on jako detektor ClickEvent, który może wystąpić w przycisku zdefiniowanym w działaniu. Ciekawe jest to, że oprócz działania (dzwoniącego), ten OtherClass będzie również powiadamiany za każdym razem, gdy nastąpi kliknięcie przycisku.


Proszę tylko ogniwo unikać odpowiedzi . Odpowiedzi, które są „niewiele więcej niż linkiem do strony zewnętrznej”, mogą zostać usunięte .
Quentin

3

Funkcja zwrotna to funkcja przekazywana (jako odwołanie lub wskaźnik) do określonej funkcji lub obiektu. Ta funkcja lub obiekt wywoła tę funkcję w dowolnym momencie później, być może wiele razy, w dowolnym celu:

  • powiadomienie o zakończeniu zadania
  • żądanie porównania dwóch elementów (jak w c qsort ())
  • raportowanie postępu procesu
  • powiadamianie o zdarzeniach
  • delegowanie instanacji obiektu
  • delegowanie malowania obszaru

...

Zatem opis wywołania zwrotnego jako funkcji wywoływanej na końcu innej funkcji lub zadania jest nadmiernie uproszczony (nawet jeśli jest to częsty przypadek użycia).


2

Wywołanie zwrotne jest pomysłem przekazania funkcji jako parametru do innej funkcji i wywołania jej po zakończeniu procesu.

Jeśli otrzymujesz koncepcję oddzwonienia poprzez niesamowite odpowiedzi powyżej, polecam powinieneś zapoznać się z tłem jej pomysłu.

„Co skłoniło ich (informatyków) do wywołania zwrotnego?” Możesz nauczyć się problemu, który polega na blokowaniu (szczególnie blokowaniu interfejsu użytkownika), a wywołanie zwrotne nie jest jedynym rozwiązaniem. Istnieje wiele innych rozwiązań (np .: Wątek, Futures, Obietnice ...).


1

Jednym z ważnych obszarów użytkowania jest zarejestrowanie jednej ze swoich funkcji jako uchwytu (tj. Oddzwanianie), a następnie wysłanie wiadomości / wywołanie jakiejś funkcji w celu wykonania pracy lub przetwarzania. Teraz po zakończeniu przetwarzania wywoływana funkcja wywoła naszą zarejestrowaną funkcję (tzn. Teraz oddzwonienie jest zakończone), co oznacza, że ​​przetwarzanie zostało zakończone.
Ten link do Wikipedii dość dobrze wyjaśnia graficznie.


1

Funkcja zwrotna, znana również jako funkcja wyższego rzędu, jest funkcją przekazywaną do innej funkcji jako parametr, a funkcja zwrotna jest wywoływana (lub wykonywana) wewnątrz funkcji nadrzędnej.

$("#button_1").click(function() {
  alert("button 1 Clicked");
});

Tutaj przekazaliśmy funkcję jako parametr do metody click. I metoda click wywoła (lub wykona) przekazaną do niej funkcję zwrotną.


1
Funkcja oddzwaniania sama w sobie nie jest funkcją wyższego rzędu. Jest przekazywany do funkcji wyższego rzędu.
Danio

1

Funkcja zwrotna Funkcja przekazana do innej funkcji jako argument.

function test_function(){       
 alert("Hello world");  
} 

setTimeout(test_function, 2000);

Uwaga: W powyższym przykładzie funkcja test_funkcja została użyta jako argument funkcji setTimeout.


1
Witamy w Stack Overflow! Przed odpowiedzią na pytanie zawsze czytaj istniejące odpowiedzi. Ta odpowiedź została już udzielona. Zamiast powtarzać odpowiedź, zagłosuj na istniejącą odpowiedź. Niektóre wskazówki dotyczące pisania dobrych odpowiedzi można znaleźć tutaj .
dferenc
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.