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?
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?
Odpowiedzi:
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]
function(arg)
) w processArray(arr,callback)
funkcji
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.
open
działa. Jest prawdopodobne, że open
moż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.
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.
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)');
}
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
}
Często oddzwanianie wymaga dostępu state
z 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
}
// 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);
Wreszcie, tutaj jest definicja closure
z 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ż:
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.
Array.prototype.forEach()
a funkcją przekazaną jako argument do setTimeout()
, i są to konie o różnych kolorach, o ile rozumujesz swój program .
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.
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)
.
set_print_callback
jest ołówekthe_callback
czy 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_computations
lub butter_the_biscuits
tak 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)
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.
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.
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.
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.
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
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) ))
grabDBValue
moż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 grabDBValue
powró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.
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
$val
w tym przypadku będzie to 6
spowodowane 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.
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.
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.
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.
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 ++”
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.
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ę var2
zostać przetworzony, a następnie przesłany jako argument?
function1(var1, function2(var2))
Jest to jeden rodzaj wywołania zwrotnego - w którym function2
wykonuje pewien kod i zwraca zmienną z powrotem do funkcji początkowej.
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.
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.
Może być o wiele więcej rzeczy. Zaangażuj uczniów, a odkryją. Mam nadzieję że to pomoże.
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:
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!
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.
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;
}
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ź.
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.
„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
Ten wzorzec służy do powiadamiania od 0 do n liczby obserwatorów / słuchaczy, że dane zadanie zostało zakończone
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.
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
[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.
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.
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.