Jak wyjaśnić callbacki zwykłym angielskim? Czym różnią się od wywoływania jednej funkcji z innej funkcji?

342

Jak wyjaśnić callbacki zwykłym angielskim? Czym różnią się od wywoływania jednej funkcji od innej, biorąc pewien kontekst z funkcji wywołującej? Jak można wyjaśnić ich moc nowicjuszowi?

Yahoo-Me
źródło
1
Uważam, że naukowa nazwa tego stylu to kontynuacja. Możesz tego poszukać na wiki.
ming_codes,
1
Jest kilka dobrych odpowiedzi do identycznym pytaniem na Quora także
DBR
Najlepsze wytłumaczenie oddzwonienia, jakie kiedykolwiek znalazłem youtube.com/watch?v=xHneyv38Jro
Sameer Sinha

Odpowiedzi:

114

Często aplikacja musi wykonywać różne funkcje w zależności od kontekstu / stanu. W tym celu używamy zmiennej, w której przechowujemy informacje o funkcji, która ma zostać wywołana. W zależności od potrzeb aplikacja ustawi tę zmienną z informacją o funkcji, która ma zostać wywołana, i wywoła funkcję przy użyciu tej samej zmiennej.

W javascript przykład znajduje się poniżej. W tym przypadku używamy argumentu metody jako zmiennej, w której przechowujemy informacje o funkcji.

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
źródło
18
Chociaż technicznie jest to wywołanie zwrotne, podane wyjaśnienie brzmi nie do odróżnienia od ogólnego wskaźnika funkcji. Przydałoby się uzasadnienie, dlaczego można użyć wywołania zwrotnego.
Eric
4
Nie rozumiem tej odpowiedzi. Czy może to być bardziej wyjaśnienie niż kod?
Abhishek Singh
dlaczego nie możemy zrobić tego, co robimy w funkcji zwanej back (the function(arg)) w processArray(arr,callback)funkcji
Abhi
1
@JoSmo masz częściowo rację. Przekaż zmienną + funkcję oddzwaniania jako parametry przyjmujące wynik utworzony za pomocą zmiennej przez funkcję oryginalną i przekaż ją w funkcji oddzwaniania do dalszego przetwarzania. przykład: func1 (a, callback_func) {v = a + 1} i istnieje predefiniowana funkcja oddzwaniania: callback_func (v) {return v + 1;} to zwiększy o 2, więc jeśli podasz argument liczby całkowitej 4 w „a”, funkcja callback_funkcja zwróci w wyniku 6. Przeczytaj to najlepsze źródło callbackhell.com, jakie znalazłem.
Dung
Ta odpowiedź nie jest jasna. Może być prostsze i wyraźniejsze!
Arjun Kalidas
545

Spróbuję uprościć to martwe. „Oddzwanianie” to dowolna funkcja wywoływana przez inną funkcję, która przyjmuje pierwszą funkcję jako parametr. Często „oddzwanianie” to funkcja wywoływana, gdy coś się dzieje. To coś można nazwać „zdarzeniem” w mowie programisty.

Wyobraź sobie ten scenariusz: spodziewasz się pakietu za kilka dni. Pakiet jest prezentem dla twojego sąsiada. Dlatego po otrzymaniu paczki należy ją zanieść sąsiadom. Jesteś poza miastem, więc zostawiasz instrukcje dla swojego małżonka.

Możesz im powiedzieć, żeby wzięli paczkę i przynieśli ją sąsiadom. Jeśli twój małżonek byłby tak głupi jak komputer, siedzieliby przy drzwiach i czekali na paczkę, aż nadejdzie (NIE RÓB NIGDY WIĘCEJ), a potem, gdy już nadejdzie, przyniosą ją sąsiadom. Ale jest lepszy sposób. Powiedz swojemu małżonkowi, że RAZ, kiedy otrzymają paczkę, powinni zanieść ją sąsiadom. Następnie mogą zacząć żyć normalnie, dopóki nie otrzymają paczki.

W naszym przykładzie odbieranie pakietu jest „zdarzeniem”, a przekazanie go sąsiadom jest „wywołaniem zwrotnym”. Współmałżonek „uruchamia” instrukcje, aby przynieść paczkę dopiero po jej przybyciu. Dużo lepiej!

Tego rodzaju myślenie jest oczywiste w codziennym życiu, ale komputery nie mają tego samego zdrowego rozsądku. Zastanów się, jak programiści zwykle piszą do pliku:

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

Tutaj OCZEKUJEMY na otwarcie pliku, zanim napiszemy do niego. To „blokuje” przepływ wykonywania, a nasz program nie może wykonać żadnej innej rzeczy, którą mógłby potrzebować! Co jeśli moglibyśmy to zrobić zamiast tego:

# 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!

Okazuje się, że robimy to z niektórymi językami i frameworkami. To całkiem fajne! Sprawdź Node.js, aby poćwiczyć trochę z takim myśleniem.

Josh Imhoff
źródło
6
Jest to poprawne, ale nie obejmuje wszystkich typowych przypadków użycia wywołań zwrotnych. Często używasz wywołań zwrotnych, gdy potrzebujesz wywołać funkcję z argumentami, które byłyby przetwarzane w procesie innej funkcji. Na przykład w PHP array_filter () i array_map () odbierają wywołania zwrotne w pętli.
Haralan Dobrev
2
Czy przykład zapisu pliku jest odpowiedni? Wydaje się przyjmować założenia, jak opendziała. Jest prawdopodobne, że openmoże zablokować się wewnętrznie, czekając, aż system operacyjny wykona swoją czarną magię, po której następuje wywołanie zwrotne. W takim przypadku nie ma różnicy w wyniku.
Kenneth K.,
23
Dobre wyjaśnienie, ale trochę się mylę. Czy funkcja oddzwaniania jest wielowątkowa?
Premraj
1
Świetny przykład! Wszędzie szukałem zwykłego angielskiego i jest to pierwszy, jaki do tej pory znalazłem :)
ChristoKiwi
1
Co sprawia, że ​​funkcja open w twoim przykładzie jest nieblokująca? otwarte mogą nadal blokować przepływ egzekucji.
Koray Tugay
82

Jak wyjaśnić callbacki zwykłym angielskim?

W zwykłym języku angielskim funkcja oddzwaniania jest jak pracownik, który „oddzwania” do swojego kierownika po zakończeniu zadania .

Czym różnią się od wywoływania jednej funkcji od innej, biorąc pewien kontekst z funkcji wywołującej?

Prawdą jest, że wywołujesz funkcję z innej funkcji, ale kluczem jest to, że wywołanie zwrotne jest traktowane jak obiekt, więc możesz zmienić funkcję, która ma zostać wywołana, w zależności od stanu systemu (np. Wzorzec projektowania strategii).

Jak można wyjaśnić ich moc nowicjuszowi?

Potęgę wywołań zwrotnych można łatwo zobaczyć na stronach internetowych w stylu AJAX, które muszą pobierać dane z serwera. Pobieranie nowych danych może trochę potrwać. Bez wywołań zwrotnych cały interfejs użytkownika „zawiesiłby się” podczas pobierania nowych danych lub trzeba odświeżyć całą stronę, a nie tylko jej część. Za pomocą wywołania zwrotnego można wstawić obraz „teraz ładuje się” i zastąpić go nowymi danymi po załadowaniu.

Niektóre kody bez oddzwaniania:

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

Z oddzwanianiem:

Oto przykład z zwrotnego, z wykorzystaniem jQuery getJSON :

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
}

Z zamknięciem:

Często oddzwanianie wymaga dostępu statez funkcji wywoływania za pomocą a closure, co jest tak, jakby pracownik potrzebował uzyskać informacje od kierownika, zanim będzie mógł wykonać swoje zadanie . Aby utworzyć closure, możesz wstawić funkcję, aby zobaczyła dane w kontekście wywołania:

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

Stosowanie:

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

Zamknięcie

Wreszcie, tutaj jest definicja closurez Douglas Crockforda :

Funkcje można zdefiniować wewnątrz innych funkcji. Funkcja wewnętrzna ma dostęp do zmiennych i parametrów funkcji zewnętrznej. Jeśli odniesienie do funkcji wewnętrznej przetrwa (na przykład jako funkcja wywołania zwrotnego), zmienne funkcji zewnętrznych również przetrwają.

Zobacz też:

użytkownik508994
źródło
12
+1. Pierwszy akapit to huk pieniędzy . Jednak reszta dość szybko przechodzi w żargon informatyki.
TarkaDaal
41

Jestem zdumiony widząc tak wielu inteligentnych ludzi, którzy nie podkreślają rzeczywistości, że słowo „oddzwanianie” zaczęło być używane na dwa niespójne sposoby.

Oba sposoby obejmują dostosowanie funkcji poprzez przekazanie dodatkowej funkcjonalności (definicji funkcji, anonimowej lub nazwanej) do istniejącej funkcji. to znaczy.

customizableFunc(customFunctionality)

Jeśli niestandardowa funkcjonalność jest po prostu podłączona do bloku kodu, dostosowałeś tę funkcję.

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

Chociaż tego rodzaju wstrzykiwana funkcjonalność jest często nazywana „oddzwanianiem”, nie ma w tym nic zależnego. Bardzo oczywistym przykładem jest metoda forEach, w której funkcja niestandardowa jest dostarczana jako argument do zastosowania do każdego elementu w tablicy w celu zmodyfikowania tablicy.

Ale zasadniczo różni się to od korzystania z funkcji „wywołania zwrotnego” do programowania asynchronicznego , jak w AJAX lub node.js, lub po prostu do przypisywania funkcji do zdarzeń interakcji użytkownika (takich jak kliknięcia myszą). W takim przypadku cały pomysł polega na odczekaniu na zdarzenie warunkowe przed wykonaniem niestandardowej funkcji. Jest to oczywiste w przypadku interakcji użytkownika, ale jest również ważne w procesach wejścia / wyjścia (wejścia / wyjścia), które mogą zająć trochę czasu, np. Odczyt plików z dysku. W tym przypadku termin „oddzwanianie” ma najbardziej oczywisty sens. Po uruchomieniu procesu we / wy (np. Proszeniu o odczytanie pliku z dysku lub serwera w celu zwrócenia danych z żądania HTTP) asynchronicznyprogram nie czeka na zakończenie. Może realizować dowolne zadania zaplanowane jako następne i reagować na niestandardowe funkcje dopiero po powiadomieniu, że plik odczytu lub żądanie HTTP zostało zakończone (lub że się nie powiedzie) i że dane są dostępne dla funkcji niestandardowej. To tak, jakby zadzwonić do firmy przez telefon i zostawić swój numer „oddzwaniania”, aby mogli do ciebie zadzwonić, gdy ktoś będzie mógł się z tobą skontaktować. To lepsze niż trzymanie się linii, kto wie, jak długo i nie będąc w stanie zająć się innymi sprawami.

Wykorzystanie asynchroniczne nieodłącznie wiąże się z pewnymi sposobami nasłuchiwania pożądanego zdarzenia (np. Zakończenie procesu we / wy), tak że gdy wystąpi (i tylko wtedy, gdy wystąpi), wykonywana jest niestandardowa funkcja „wywołania zwrotnego”. W oczywistym przykładzie AJAX, kiedy dane faktycznie docierają z serwera, uruchamiana jest funkcja „oddzwaniania”, która wykorzystuje te dane do modyfikowania DOM, a zatem przerysowuje okno przeglądarki do tego stopnia.

Przypomnę. Niektóre osoby używają słowa „wywołanie zwrotne” w odniesieniu do dowolnej niestandardowej funkcji, którą można wprowadzić jako argument do istniejącej funkcji. Ale, przynajmniej dla mnie, najbardziej odpowiednim użyciem tego słowa jest to, że wstrzyknięta funkcja „wywołania zwrotnego” jest używana asynchronicznie - do wykonania tylko w przypadku wystąpienia zdarzenia, o którym oczekuje na powiadomienie.

Robert Polevoi
źródło
Kiedy funkcja oddzwoni, dokąd wraca proces? Na przykład, jeśli są cztery linie kodu; 1. fileObject = open (plik, writeToFile); 2. doSomething1 (); 3. doSomething2 (); 4. doSomething3 ().
MagicLAMP,
Linia 1 jest wykonywana, ale zamiast czekać na otwarcie pliku, przechodzi do linii 2, a następnie 3. W tym momencie plik się otwiera i (gdy linia 3 zakończy operację semafora) oddzwania do licznika programu, aby powiedzieć „ przekazuje sterowanie do writeToFile ”, który robi rzeczy, a gdy jest zakończony, przekazuje kontrolę z powrotem do punktu, w którym INT wystąpił w linii 3, lub do linii 4, jeśli linia 3 ma się skończyć.
MagicLAMP,
1
Jest to bardzo wyraźne wyjaśnienie innego ważnego punktu: np. Różnica między funkcją przekazaną jako argument do Array.prototype.forEach()a funkcją przekazaną jako argument do setTimeout(), i są to konie o różnych kolorach, o ile rozumujesz swój program .
mikermcneil
26

W kategoriach nieprogramowych wywołanie zwrotne jest wypełnianiem pustego pola w programie.

Powszechnym elementem wielu formularzy papierowych jest „Osoba, do której można zadzwonić w nagłych przypadkach”. Jest tam pusta linia. Piszesz czyimś imieniem i numerem telefonu. Jeśli zdarzy się nagły wypadek, osoba ta zostanie wezwana.

  • Każdy dostaje ten sam pusty formularz, ale
  • Każdy może napisać inny numer telefonu alarmowego.

To jest klucz. Nie zmieniasz formularza (kodu, zwykle cudzego). Możesz jednak uzupełnić brakujące informacje ( swój numer).

Przykład 1:

Wywołania zwrotne są używane jako niestandardowe metody, prawdopodobnie w celu dodania / zmiany zachowania programu. Na przykład weź kod C, który wykonuje funkcję, ale nie wie, jak wydrukować wynik. Wszystko, co może zrobić, to zrobić ciąg. Kiedy próbuje dowiedzieć się, co zrobić z łańcuchem, widzi pustą linię. Ale programista dał ci puste miejsce na napisanie twojego callbacka!

W tym przykładzie nie używasz ołówka do wypełniania pustego miejsca na kartce papieru, używasz funkcji set_print_callback(the_callback).

  • Pusta zmienna w module / kodzie to pusta linia,
  • set_print_callback jest ołówek
  • i the_callbackczy podajesz swoje informacje.

Wypełniłeś teraz ten pusty wiersz w programie. Ilekroć potrzebuje wydrukować, spojrzy na tę pustą linię i postępuje zgodnie z instrukcjami tam zawartymi (tj. Wywołuje funkcję, którą tam umieściłeś). W praktyce pozwala to na drukowanie z ekranu, do pliku dziennika, na drukarkę, przez połączenie sieciowe lub dowolną ich kombinację. Wypełniłeś puste miejsce tym, co chcesz zrobić.

Przykład 2:

Kiedy zostaniesz poinformowany, że musisz zadzwonić pod numer alarmowy, idź i przeczytaj to, co jest zapisane na papierowym formularzu, a następnie zadzwoń pod numer, który czytasz. Jeśli ten wiersz jest pusty, nic nie zostanie zrobione.

Programowanie GUI działa podobnie. Po kliknięciu przycisku program musi dowiedzieć się, co dalej. Idzie i szuka oddzwonienia. To wywołanie zwrotne jest puste: „Oto, co robisz po kliknięciu przycisku Button1”

Większość IDE automatycznie wypełni dla ciebie puste pole (napisz podstawową metodę), kiedy o to poprosisz (np button1_clicked.). Jednak ten blank może mieć dowolną metodę, którą dobrze sobie radzisz . Możesz wywołać metodę run_computationslub butter_the_biscuitstak długo, jak wstawisz nazwę tego wywołania zwrotnego w odpowiednie puste miejsce. Możesz wpisać „555-555-1212” w polu numeru alarmowego pustym. To nie ma większego sensu, ale jest dopuszczalne.


Ostatnia uwaga: ta pusta linia, którą wypełniasz oddzwanianiem? Można go usunąć i ponownie napisać do woli. (to, czy powinieneś czy nie, to kolejne pytanie, ale to część ich mocy)

JoeyG
źródło
22

Zawsze lepiej zacząć od przykładu :).

Załóżmy, że masz dwa moduły A i B.

Chcesz, aby moduł A był powiadamiany, gdy jakieś zdarzenie / warunek wystąpi w module B. Jednak moduł B nie ma pojęcia o module A. Wszystko, co wie, to adres do określonej funkcji (modułu A) poprzez wskaźnik funkcji, który jest dostarczone mu przez moduł A.

Zatem wszystko, co B musi teraz zrobić, to „oddzwonienie” do modułu A, gdy wystąpi określone zdarzenie / warunek przy użyciu wskaźnika funkcji. A może wykonać dalsze przetwarzanie wewnątrz funkcji zwrotnej.

*) Oczywistą zaletą jest to, że wyodrębniasz wszystko o module A z modułu B. Moduł B nie musi się przejmować kim / czym jest moduł A.

Gargi Srinivas
źródło
Zatem parametry funkcji w A są podane w module B, czy to prawda?
U.Savas
21

Wyobraź sobie, że potrzebujesz funkcji, która zwraca 10 do kwadratu, więc napiszesz funkcję:

function tenSquared() {return 10*10;}

Później potrzebujesz 9 do kwadratu, więc napisz inną funkcję:

function nineSquared() {return 9*9;}

Ostatecznie zastąpisz je ogólną funkcją:

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

Dokładnie to samo dotyczy oddzwaniania. Masz funkcję, która coś robi, a po zakończeniu wywołuje doA:

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

Później chcesz, aby ta sama funkcja wywoływała doB, zamiast tego możesz powielić całą funkcję:

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

Lub możesz przekazać funkcję wywołania zwrotnego jako zmienną i musisz mieć tę funkcję tylko raz:

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

Następnie wystarczy wywołać compute (doA) i compute (doB).

Poza kodem upraszczającym, kod asynchroniczny informuje, że został on ukończony, wywołując dowolną funkcję po zakończeniu, podobnie jak w przypadku połączenia z inną osobą przez telefon i pozostawienia numeru zwrotnego.

Brian Nickel
źródło
Więc przekazujesz funkcję jako parametr. Czy cały język programowania umożliwia przekazywanie funkcji jako parametru? Jeśli nie, to czy możesz pokazać przykład, w jaki sposób implementujesz funkcję zwrotną w tym języku.
Quazi Irfan
21

Johny programista potrzebuje zszywacza, więc idzie do działu zaopatrzenia biura i prosi o jeden, po wypełnieniu formularza zamówienia może albo stać tam i czekać, aż urzędnik rozejrzy się po magazynie zszywacza (jak wywołanie funkcji blokującej ) lub w międzyczasie zrób coś innego.

ponieważ zwykle zajmuje to trochę czasu, John umieszcza notatkę wraz z formularzem prośby, aby zadzwonili do niego, gdy zszywacz będzie gotowy do odbioru, więc w międzyczasie może zrobić coś innego jak drzemkę na biurku.

tovmeod
źródło
1
Wydaje się to bardziej zbliżone do obietnic niż do oddzwaniania: blog.jcoglan.com/2013/03/30/…
Thomas
2
Obietnice to po prostu cukier składniowy wokół wywołań zwrotnych.
Deven Phillips
11

Czujesz się chory, więc idziesz do lekarza. Bada cię i stwierdza, że ​​potrzebujesz lekarstw. Przepisuje leki i wzywa receptę do lokalnej apteki. Ty idź do domu. Później apteka dzwoni z informacją, że recepta jest gotowa. Idź i podnieś to.

wizerunek
źródło
Bardzo dobra analogia. Czy możesz rozwinąć nieco dalej, być może z przykładami (prostymi) związanymi z programowaniem?
a20
10

Istnieją dwa punkty do wyjaśnienia, jeden to, w jaki sposób działa wywołanie zwrotne (przekazywanie funkcji, którą można wywołać bez znajomości jej kontekstu), a drugi to, do czego służy (asynchroniczna obsługa zdarzeń).

Analogia oczekiwania na dostawę paczki, którą wykorzystały inne odpowiedzi, jest dobra na wyjaśnienie obu. W programie komputerowym powiedziałbyś komputerowi, aby spodziewał się paczki. Zwykle siedziałby tam i czekał (i nic więcej nie zrobił), aż paczka dotrze, być może w nieskończoność, jeśli nigdy nie dotrze. Dla ludzi brzmi to głupio, ale bez dalszych środków jest to całkowicie naturalne dla komputera.

Oddzwonieniem byłby dzwonek przy drzwiach wejściowych. Zapewniasz paczkom sposób powiadamiania o przybyciu paczki bez konieczności dowiedzenia się, gdzie jesteś (nawet jeśli) jesteś w domu lub jak działa dzwonek. (Na przykład niektóre „dzwonki” faktycznie wysyłają połączenie telefoniczne.) Ponieważ udostępniłeś „funkcję oddzwaniania”, którą można „wywołać” w dowolnym momencie, poza kontekstem, możesz teraz przestać siedzieć na werandzie i „obsłużyć zdarzenie ”(przybycia paczki), kiedy tylko nadejdzie czas

Hanno Fietz
źródło
To jest po prostu angielski.
Jeb50
To wydaje się być najlepszym wytłumaczeniem!
Vishal Sharma,
6

Wyobraź sobie, że przyjaciółka opuszcza twój dom, a ty mówisz jej: „Zadzwoń do mnie, kiedy wrócisz do domu, abym wiedział, że dotarłeś bezpiecznie”; to jest (dosłownie) oddzwonienie . Taka jest funkcja oddzwaniania, niezależnie od języka. Chcesz, aby jakaś procedura przekazała ci kontrolę, kiedy wykonała jakieś zadanie, więc dajesz jej funkcję, która pozwala ci oddzwonić.

Na przykład w Pythonie

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

grabDBValuemożna zapisać tylko w celu pobrania wartości z bazy danych, a następnie pozwala określić, co faktycznie zrobić z wartością, więc akceptuje funkcję. Nie wiesz, kiedy lub czy grabDBValuepowróci, ale jeśli / kiedy to nastąpi, wiesz, co chcesz zrobić. Tutaj przekazuję anonimową funkcję (lub lambda ), która wysyła wartość do okna GUI. Mogę łatwo zmienić zachowanie programu, wykonując następujące czynności:

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

Oddzwaniania działają dobrze w językach, w których funkcje są pierwszorzędnymi wartościami, tak jak zwykłe liczby całkowite, ciągi znaków, booleany itp. W C można „przekazać” funkcję, przekazując do niej wskaźnik, a osoba dzwoniąca może jej użyć; w Javie program wywołujący poprosi o klasę statyczną określonego typu o określonej nazwie metody, ponieważ poza klasami nie ma żadnych funkcji („metod”); aw większości innych dynamicznych języków możesz przekazać funkcję o prostej składni.

Protip:

W językach z zasięgiem leksykalnym (takich jak Scheme lub Perl) możesz wyciągnąć taki trick:

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

$valw tym przypadku będzie to 6spowodowane tym, że wywołanie zwrotne ma dostęp do zmiennych zadeklarowanych w środowisku leksykalnym, w którym zostało zdefiniowane. Zakres leksykalny i anonimowe wywołania zwrotne to potężne połączenie, które gwarantuje dalsze studia dla początkującego programisty.

gatlin
źródło
1
+1. Właściwie podoba mi się ta odpowiedź. Wyjaśnienie czym jest zwrotna jest , jest prosty i zwięzły.
TarkaDaal
6

Masz kod, który chcesz uruchomić. Zwykle, kiedy go wywołujesz, czekasz, aż zakończy się, zanim przejdziesz dalej (co może spowodować, że twoja aplikacja stanie się szara / wywoła czas obracania kursora).

Alternatywną metodą jest równoległe uruchomienie tego kodu i kontynuowanie własnej pracy. Ale co jeśli twój oryginalny kod musi robić różne rzeczy w zależności od odpowiedzi z kodu, który wywołał? Cóż, w takim przypadku możesz podać nazwę / lokalizację kodu, który ma wywoływać po zakończeniu. To jest „oddzwonienie”.

Normalny kod: Zapytaj o informacje-> Informacje o procesie-> Zajmij się wynikami przetwarzania-> Kontynuuj robienie innych rzeczy.

Z oddzwanianiem: Poproś o informacje-> Informacje o procesie-> Kontynuuj robienie innych rzeczy. A w późniejszym momencie-> Zajmij się wynikami Przetwarzania.

Andrew Ducker
źródło
6

Bez wywołania zwrotnego ani innych specjalnych zasobów programistycznych (takich jak wątki i inne), program jest dokładnie sekwencją instrukcji, które są wykonywane sekwencyjnie jedna po drugiej , a nawet z rodzajem „zachowania dynamicznego” określonego przez pewne warunki, wszystkie możliwe scenariusze powinny być wcześniej zaprogramowane .

Jeśli więc musimy zapewnić programowi rzeczywiste zachowanie dynamiczne, możemy użyć wywołania zwrotnego. Za pomocą wywołania zwrotnego możesz instruować według parametrów, program, aby wywołać inny program, podając niektóre wcześniej zdefiniowane parametry i może oczekiwać pewnych wyników ( jest to podpis umowy lub operacji ), dzięki czemu wyniki te mogą zostać wygenerowane / przetworzone przez program innej firmy, który nie był wcześniej znany.

Ta technika jest podstawą polimorfizmu stosowanego w programach, funkcjach, obiektach i wszystkich innych jednostkach kodu obsługiwanych przez komputery.

Ludzki świat użyty jako przykład do oddzwaniania jest miło wyjaśniony, kiedy wykonujesz jakąś pracę, załóżmy, że jesteś malarzem ( tutaj jesteś głównym programem, który maluje ) i czasami zadzwoń do klienta, aby poprosił go o zatwierdzenie wyniku twojej pracy , więc decyduje, czy zdjęcie jest dobre ( Twój klient to program innej firmy ).

W powyższym przykładzie jesteś malarzem i „delegujesz” innym zadanie do zatwierdzenia wyniku, obraz jest parametrem, a każdy nowy klient (funkcja „oddzwaniana”) zmienia wynik twojej pracy decydując, czego chce o zdjęciu ( decyzje podejmowane przez klientów są zwracane z „funkcji zwrotnej” ).

Mam nadzieję, że to wyjaśnienie może być przydatne.

Luciano
źródło
6

Udawajmy, że dałeś mi potencjalnie długofalowe zadanie: poznaj nazwiska pierwszych pięciu unikalnych ludzi, z którymi się spotykasz. Może to potrwać dni, jeśli jestem w słabo zaludnionym obszarze. Naprawdę nie interesuje Cię siedzenie na rękach, kiedy biegam, więc mówisz: „Kiedy masz listę, zadzwoń do mnie i odczytaj mi ją. Oto numer”.

Podałeś mi odwołanie zwrotne - funkcję, którą mam wykonać, aby przekazać dalsze przetwarzanie.

W JavaScript może to wyglądać mniej więcej tak:

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

Prawdopodobnie można to poprawić na wiele sposobów. Na przykład możesz podać drugie oddzwonienie: jeśli zajmie to więcej niż godzinę, zadzwoń na czerwony telefon i powiedz osobie, która odbierze, że upłynął limit czasu.

parowiec 25
źródło
6

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 ++”

DejanLekic
źródło
Łatwiejszym wyjaśnieniem byłoby: dzwonię do kogoś, jest na spotkaniu, zostawiam numer telefonu, ona oddzwania.
Samotny
5

Oddzwonienie to funkcja, która zostanie wywołana przez drugą funkcję. Ta druga funkcja nie wie z góry, jaką funkcję wywoła. Tak więc tożsamość funkcji zwrotnej jest gdzieś przechowywana lub przekazywana do drugiej funkcji jako parametr. Ta „tożsamość”, w zależności od języka programowania, może być adresem oddzwaniania lub innym wskaźnikiem lub może być nazwą funkcji. Zasada jest taka sama, przechowujemy lub przekazujemy pewne informacje, które jednoznacznie identyfikują funkcję.

Kiedy nadejdzie czas, druga funkcja może wywołać oddzwonienie, podając parametry w zależności od okoliczności w danym momencie. Może nawet wybrać połączenie zwrotne z zestawu możliwych połączeń zwrotnych. Język programowania musi zapewniać jakąś składnię, aby umożliwić drugiej funkcji wywołanie zwrotne, znając jej „tożsamość”.

Ten mechanizm ma wiele możliwych zastosowań. W przypadku wywołań zwrotnych projektant funkcji może pozwolić, aby była dostosowywana przez wywoływanie wszystkich podanych wywołań zwrotnych. Na przykład funkcja sortująca może przyjmować funkcję zwrotną jako parametr, a ta funkcja zwrotna może być funkcją służącą do porównywania dwóch elementów, aby zdecydować, który z nich jest pierwszy.

Nawiasem mówiąc, w zależności od języka programowania słowo „funkcja” w powyższej dyskusji może zostać zastąpione przez „blok”, „zamknięcie”, „lambda” itp.

David Casseres
źródło
5

Zwykle wysyłamy zmienne do funkcji. Załóżmy, że masz zadanie, w którym zmienna musi zostać przetworzona przed podaniem jej jako argumentu - możesz użyć wywołania zwrotnego.

function1(var1, var2) jest zwykłym sposobem.

Co jeśli chcę var2zostać przetworzony, a następnie przesłany jako argument? function1(var1, function2(var2))

Jest to jeden rodzaj wywołania zwrotnego - w którym function2wykonuje pewien kod i zwraca zmienną z powrotem do funkcji początkowej.

Nishant
źródło
2
Jaki jest inny rodzaj oddzwaniania?
Johnny
@johnny: Istnieją normalne wywołania zwrotne przeglądarki, które są uruchamiane po ukończeniu Ajax itp.
Nishant
4

Metaforyczne wyjaśnienie:

Mam paczkę, którą chcę dostarczyć przyjacielowi, a także chcę wiedzieć, kiedy mój przyjaciel ją odbierze.

Zabieram paczkę na pocztę i proszę, by ją doręczyła. Jeśli chcę wiedzieć, kiedy mój przyjaciel odbierze paczkę, mam dwie opcje:

(a) Mogę poczekać na poczcie, aż zostanie dostarczona.

(b) Otrzymam wiadomość e-mail, gdy zostanie dostarczona.

Opcja (b) jest analogiczna do oddzwaniania.

tonylo
źródło
4

Aby uczyć oddzwaniania, najpierw musisz nauczyć wskaźnik. Gdy uczniowie zrozumieją pojęcie wskaźnika do zmiennej, pomysł wywołania zwrotnego stanie się łatwiejszy. Zakładając, że używasz C / C ++, możesz wykonać następujące kroki.

  • Najpierw pokaż uczniom, jak posługiwać się zmiennymi i manipulować nimi za pomocą wskaźników, a także używać normalnych identyfikatorów zmiennych.
  • Następnie naucz ich, że są rzeczy, które można zrobić tylko za pomocą wskaźników (np. Przekazywanie zmiennej przez referencję).
  • Następnie powiedz im, jak wykonywalny kod lub funkcje są jak niektóre inne dane (lub zmienne) w pamięci. Funkcje mają więc również adresy lub wskaźniki.
  • Następnie pokaż im, w jaki sposób można wywoływać funkcje za pomocą wskaźników funkcji i powiedz, że nazywają się callbackami.
  • Pytanie brzmi: dlaczego wszystkie te kłopoty z wywoływaniem niektórych funkcji? Jaka jest korzyść? Podobnie jak wskaźniki danych, funkcja callback zwana także callbackami ma pewne zalety w porównaniu do używania normalnych identyfikatorów.
  • Po pierwsze, identyfikatory funkcji lub nazwy funkcji nie mogą być używane jako normalne dane. Mam na myśli, że nie można utworzyć struktury danych z funkcjami (takimi jak tablica lub połączona lista funkcji). Ale za pomocą wywołań zwrotnych możesz utworzyć tablicę, połączoną listę lub użyć ich z innymi danymi, np. W słowniku par klucz-wartość lub drzew, lub dowolnymi innymi rzeczami. To ogromna korzyść. A inne świadczenia są w rzeczywistości dzieckiem tego.
  • Najczęstsze użycie wywołań zwrotnych występuje w programowaniu sterowników zdarzeń. Gdzie jedna lub więcej funkcji jest wykonywanych na podstawie jakiegoś przychodzącego sygnału. Dzięki wywołaniom zwrotnym można utrzymywać słownik do mapowania sygnałów za pomocą wywołań zwrotnych. Wtedy rozdzielczość sygnału wejściowego i wykonanie odpowiedniego kodu stają się znacznie łatwiejsze.
  • Drugim zastosowaniem wywołań zwrotnych, które przychodzą mi do głowy, są funkcje wyższego rzędu. Funkcje, które przyjmują inne funkcje jako argumenty wejściowe. Aby wysyłać funkcje jako argumenty, potrzebujemy wywołań zwrotnych. Przykładem może być funkcja pobierająca tablicę i wywołanie zwrotne. Następnie wykonuje wywołanie zwrotne dla każdego elementu tablicy i zwraca wyniki w innej tablicy. Jeśli przekażemy funkcji podwójne wywołanie zwrotne, otrzymamy tablicę o podwójnej wartości. Jeśli miniemy kwadratowe wywołanie zwrotne, otrzymamy kwadraty. W przypadku pierwiastków kwadratowych wystarczy wysłać odpowiednie wywołanie zwrotne. Nie można tego zrobić przy normalnych funkcjach.

Może być o wiele więcej rzeczy. Zaangażuj uczniów, a odkryją. Mam nadzieję że to pomoże.

Gulszan
źródło
1
Moja kolejna odpowiedź związana z tym tematem w programmers.SE programmers.stackexchange.com/a/75449/963
Gulshan
3

W zwykłym angielskim oddzwonienie jest obietnicą. Joe, Jane, David i Samantha dzielą się wspólnym przejazdem do pracy. Joe dzisiaj prowadzi. Jane, David i Samantha mają kilka opcji:

  1. Co 5 minut sprawdzaj okno, aby sprawdzić, czy Joe nie ma
  2. Rób swoje, dopóki Joe nie zadzwoni dzwonkiem do drzwi.

Opcja 1: To bardziej przypomina sondaż, w którym Jane utknęła w „pętli” sprawdzającej, czy Joe jest na zewnątrz. Tymczasem Jane nie może zrobić nic więcej.

Opcja 2: jest to przykład wywołania zwrotnego. Jane mówi Joe'emu, żeby zadzwonił do drzwi, kiedy jest na zewnątrz. Daje mu „funkcję”, by zadzwonić dzwonkiem do drzwi. Joe nie musi wiedzieć, jak działa dzwonek do drzwi ani gdzie on jest, musi tylko wywołać tę funkcję, tj. Zadzwonić dzwonkiem do drzwi, kiedy tam będzie.

Połączenia zwrotne są sterowane przez „zdarzenia”. W tym przykładzie „wydarzeniem” jest przybycie Joe. Na przykład w Ajax zdarzenia mogą być „sukcesem” lub „niepowodzeniem” żądania asynchronicznego i każde może mieć takie same lub różne wywołania zwrotne.

Pod względem aplikacji JavaScript i wywołań zwrotnych. Musimy także zrozumieć „zamknięcia” i kontekst aplikacji. To, do czego odnosi się „to”, może łatwo pomylić programistów JavaScript. W tym przykładzie w ramach metody / wywołania zwrotnego każdej osoby „ring_the_door_bell ()” mogą istnieć inne metody, które każda osoba musi wykonać na podstawie porannej rutyny, np. "wyłączyć telewizor()". Chcielibyśmy, aby „to” odnosiło się do obiektu „Jane” lub obiektu „David”, aby każdy mógł skonfigurować wszystko, co trzeba zrobić, zanim Joe je odbierze. W tym przypadku konfiguracja wywołania zwrotnego z Joe wymaga parodowania metody, aby „to” odnosiło się do odpowiedniego obiektu.

Mam nadzieję, że to pomaga!

Nael El Shawwa
źródło
3

Oddzwonienie jest kopertą z zaadresowanym adresem. Kiedy wywołujesz funkcję, to jest jak wysyłanie listu. Jeśli chcesz, aby ta funkcja wywoływała inną funkcję, podaj tę informację w formie referencji lub adresu.

pete
źródło
3

Myślę, że wyjaśnienie tego jest dość łatwe.

Przy pierwszym wywołaniu zwrotnym są zwykłe funkcje.
I dalej: wywołujemy tę funkcję (nazwijmy ją A) z wnętrza innej funkcji (nazwijmy to B).

Magia polega na tym, że decyduję, która funkcja powinna zostać wywołana przez funkcję spoza B.

W tym momencie piszę funkcję BI, nie wiem, która funkcja zwrotna powinna zostać wywołana. W momencie, gdy wywołuję funkcję BI, mówię również tej funkcji, aby wywołała funkcję A. To wszystko.

Junzen
źródło
3

Co to jest funkcja oddzwaniania?

Prosta odpowiedź na to pierwsze pytanie brzmi: funkcja wywołania zwrotnego jest funkcją wywoływaną 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 nią, że mówi się, że oddzwanianie zostało wykonane.

Funkcja oddzwaniania jest trudna do prześledzenia, ale czasami jest bardzo przydatna. Zwłaszcza podczas projektowania bibliotek. Funkcja oddzwaniania jest jak proszenie użytkownika o podanie nazwy funkcji, a funkcja ta będzie wywoływana pod pewnymi warunkami.

Na przykład piszesz licznik oddzwaniania. Pozwala określić czas trwania i funkcję do wywołania, a funkcja będzie odpowiednio oddzwanianiem. „Uruchom myfunction () co 10 sekund przez 5 razy”

Możesz też utworzyć katalog funkcji, przekazując listę nazw funkcji i poprosić bibliotekę o odpowiednie wywołanie zwrotne. „Powodzenie oddzwaniania () w przypadku powodzenia, oddzwanianie nie powiodło się () w przypadku niepowodzenia.”

Przyjrzyjmy się prostemu przykładowi wskaźnika funkcji

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

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

Jak przekazać argument do funkcji zwrotnej?

Zaobserwowano, że wskaźnik funkcji do implementacji wywołania zwrotnego przyjmuje wartość void *, co oznacza, że ​​może przyjmować dowolny typ zmiennej, w tym strukturę. Dlatego możesz przekazać wiele argumentów według struktury.

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
źródło
2

Oddzwonienie to metoda zaplanowana do wykonania po spełnieniu warunku.

Przykładem „prawdziwego świata” jest lokalny sklep z grami wideo. Czekasz na Half-Life 3. Zamiast codziennie odwiedzać sklep, aby sprawdzić, czy jest dostępny, rejestrujesz swój e-mail na liście, aby otrzymywać powiadomienia o dostępności gry. E-mail staje się „oddzwanianiem”, a warunkiem, który należy spełnić, jest dostępność gry.

Przykład „programistów” to strona internetowa, na której chcesz wykonać akcję po kliknięciu przycisku. Zarejestruj przycisk wywołania zwrotnego dla przycisku i kontynuuj wykonywanie innych zadań. Kiedy / jeśli użytkownik kliknie przycisk, przeglądarka spojrzy na listę wywołań zwrotnych dla tego zdarzenia i wywoła twoją metodę.

Oddzwonienie to sposób na asynchroniczne obsługiwanie zdarzeń. Nigdy nie wiadomo, kiedy oddzwonienie zostanie wykonane, czy w ogóle zostanie wykonane. Zaletą jest to, że zwalnia program i procesor CPU do wykonywania innych zadań podczas oczekiwania na odpowiedź.

Optymista
źródło
Powiedzenie, że jest „zaplanowane”, może powodować zamieszanie. Oddzwanianie jest często używane w systemach asynchronicznych i nie byłoby „harmonogramu”, lecz „zdarzenie”, które wyzwala wywołanie zwrotne do wykonania.
Deven Phillips
2

Prosty i prosty: oddzwonienie to funkcja, którą nadajesz innej funkcji, aby mogła ją wywołać .

Zwykle jest wywoływany po zakończeniu niektórych operacji. Ponieważ tworzysz wywołanie zwrotne przed przekazaniem go innej funkcji, możesz go zainicjować za pomocą informacji kontekstowych z witryny wywoływania. Dlatego nazywa się call * back * - pierwsza funkcja wywołuje z powrotem do kontekstu, z którego została wywołana.

Andrei Vajna II
źródło
2

„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 funkcji, która nie przyjmuje argumentów i zwraca liczbę całkowitą. NIE zapomnij wstawić w nawiasach, w przeciwnym razie kompilator przyjmie, że ptrFunc jest normalną nazwą funkcji, która nic nie bierze 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 w rejestrze 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 komponenty 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ę 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), OtherClass będzie również powiadamiany za każdym razem, gdy zdarzenie kliknięcia wystąpi na przycisku.

somenath mukhopadhyay
źródło
1

Oddzwanianie pozwala wstawić własny kod do innego bloku kodu, który ma zostać wykonany w innym czasie, który modyfikuje lub dodaje zachowanie tego innego bloku kodu do własnych potrzeb. Zyskujesz elastyczność i możliwość dostosowywania, a jednocześnie możesz mieć łatwiejszy do utrzymania kod.

Mniej kodu twardego = łatwiejsza konserwacja i zmiana = mniej czasu = większa wartość biznesowa = wspaniałość.

Na przykład w javascript przy użyciu Underscore.js można znaleźć wszystkie parzyste elementy w tablicy takiej jak ta:

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

Przykład dzięki uprzejmości Underscore.js: http://documentcloud.github.com/underscore/#filter

letuboy
źródło
1

[edytowane], gdy mamy dwie funkcje powiedzieć funkcjonalnie urządzonych i functionB , jeśli funkcjonalnie urządzonych zależy functionB .

następnie wywołujemy funkcję B jako funkcję wywołania zwrotnego. Jest to szeroko stosowane w środowisku Spring.

przykładowa funkcja Wikipedii

Balaswamy Vaddeman
źródło
1

Pomyśl o metodzie jako o zadaniu dla współpracownika. Proste zadanie może wyglądać następująco:

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

Twój współpracownik pilnie wykonuje matematykę i daje następujący wynik:

x = -6
y = -4

Ale twój współpracownik ma problem, nie zawsze rozumie takie zapisy ^, ale rozumie je po ich opisie. Takich jak exponent. Za każdym razem, gdy znajdzie jeden z nich, otrzymasz:

I don't understand "^"

Wymaga to ponownego przepisania całego zestawu instrukcji po wyjaśnieniu znaczenia postaci dla współpracownika, a on nie zawsze pamięta między pytaniami. Ma też trudności z zapamiętaniem twoich wskazówek, takich jak po prostu zapytaj mnie. Zawsze jednak postępuje zgodnie z twoimi pisemnymi wskazówkami, najlepiej jak potrafi.

Zastanawiasz się nad rozwiązaniem, po prostu dodajesz następujące instrukcje do wszystkich instrukcji:

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

Teraz, gdy ma problem, dzwoni do ciebie i pyta, zamiast dać złą odpowiedź i ponownie uruchomić proces.

Guvante
źródło
0

To w zakresie pobierania strony internetowej:

Twój program działa na telefonie komórkowym i wymaga strony internetowej http://www.google.com . Jeśli piszesz program synchronicznie, funkcja, którą piszesz, aby pobrać dane, będzie działała nieprzerwanie, dopóki wszystkie dane nie zostaną pobrane. Oznacza to, że interfejs użytkownika nie odświeży się i zasadniczo będzie wyglądał na zamrożony. Jeśli piszesz program za pomocą wywołań zwrotnych, żądasz danych i mówisz „uruchom tę funkcję po zakończeniu”. To pozwala interfejsowi użytkownika nadal pozwalać na interakcję użytkownika podczas pobierania pliku. Po zakończeniu pobierania strony wywoływana jest funkcja wyniku (oddzwanianie) i można obsługiwać dane.

Zasadniczo pozwala ci poprosić o coś i kontynuować wykonywanie w oczekiwaniu na wynik. Gdy wynik wróci do Ciebie za pomocą funkcji oddzwaniania, możesz wybrać operację, w której została przerwana.

sokket
źródło