Dlaczego potrzebujemy „funkcji oddzwaniania”?

16

Czytam książkę programming in Lua. Powiedział tak

Zamknięcia stanowią cenne narzędzie w wielu kontekstach. Jak widzieliśmy, są one przydatne jako argumenty dla funkcji wyższego rzędu, takich jak sortowanie. Zamknięcia są cenne dla funkcji, które również budują inne funkcje, jak na przykład nasz nowy licznik; ten mechanizm pozwala programom Lua na stosowanie zaawansowanych technik programowania ze świata funkcjonalnego. Zamknięcia są również przydatne w przypadku funkcji oddzwaniania. Typowy przykład występuje tutaj, gdy tworzysz przyciski w konwencjonalnym zestawie narzędzi GUI. Każdy przycisk ma funkcję wywołania zwrotnego, którą należy wywołać, gdy użytkownik naciśnie przycisk; chcesz, aby różne przyciski wykonywały nieco inne czynności po naciśnięciu. Na przykład kalkulator cyfrowy potrzebuje dziesięciu podobnych przycisków, po jednym dla każdej cyfry. Możesz utworzyć każdy z nich za pomocą funkcji takiej jak ta:

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

Wygląda na to, że jeśli zadzwonię digitButton, zwróci action(spowoduje to zamknięcie), więc mogę uzyskać dostęp do digitprzekazanego digitButton.

Moje pytanie brzmi:

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


Autor powiedział:

W tym przykładzie zakładamy, że Button to funkcja zestawu narzędzi, która tworzy nowe przyciski; etykieta to etykieta przycisku; a działanie to zamknięcie oddzwaniania, które ma zostać wywołane po naciśnięciu przycisku. Oddzwonienie można wywołać długo po tym, jak digitButton wykonał swoje zadanie i po tym, jak cyfra zmiennej lokalnej wyszła poza zakres, ale nadal może uzyskać dostęp do tej zmiennej.

według autora, myślę, że podobny przykład jest taki:

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

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

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

click_action = digitButton(10)
click_action()

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

Lucas Li
źródło

Odpowiedzi:

45

Facet 1 do Facet 2: hej koleś Chcę coś zrobić, gdy użytkownik kliknie tam, oddzwoń, kiedy to się stanie, dobrze?

Guy 2 oddzwania do Guy 1, gdy użytkownik kliknie tutaj.

Florian Margaine
źródło
1
Bardzo podoba mi się ten żart z oddzwanianiem :-P
Lucas Li
6
Czy to tylko ja? Nie rozumiem, jak to wyjaśnia, dlaczego potrzebujemy funkcji oddzwaniania.
phant0m
2
To bardziej jak Guy 1 mówi Guy 2 „kiedy nadejdzie właściwy czas, zrób to”. Facet 2 opowiada o swoim dniu i robi to w odpowiednim momencie. Jeśli chciałeś, aby Facet 1 był osobą dzwoniącą do Faceta 2, oznacza to, że jest to niepoprawne, ponieważ Facet 2 nie oddzwoni do Faceta 1, więc woła do zrobienia. W rzeczywistości, jeśli Guy 1 jest osobą dzwoniącą do Guy 2, miałbyś twardą pętlę na ręce w tym scenariuszu. Jeśli chciałeś, aby Guy 1 był oddzwanianiem, jest to niepoprawne, ponieważ oddzwanianie nie ma pojęcia, kim jest Guy 2 i na pewno nie prosi go o oddzwonienie.
ryz
To okropne wytłumaczenie.
środku w dniu
21

Nie, to nigdy nie zwróci akcji. Przycisk wykona go, gdy użytkownik go kliknie. I to jest odpowiedź. Potrzebujesz funkcji zwrotnych, jeśli chcesz zdefiniować działania, które powinny się zdarzyć w innym komponencie w reakcji na zdarzenie, którego nie kontrolujesz (pętla zdarzeń, która jest częścią systemu, wykona metodę przycisku, która z kolei wykona akcja).

Jan Hudec
źródło
Nie zgadzam się z tobą Zredagowałem swój post i dodałem kilka kodów. Nadal uważam, że akcja nie jest wykonywana natychmiast.
Lucas Li
2
Tim, masz rację - ale to też Jan. „Przycisk wykona go, gdy użytkownik go kliknie”, nie od razu.
AlexanderBrevig
@AlexanderBrevig tak, nadal uważam, że odpowiedź Jana jest dla mnie bardzo przydatna. Dzięki temu wiem, co to jest oddzwonienie i dlaczego potrzebujemy oddzwonienia.
Lucas Li
Oddzwonienie to po prostu brudna nazwa, którą ktoś dodał do drugiej funkcji, która działa, gdy pierwsza jest uruchamiana i być może oparta na kryteriach. Wywołaj swoją funkcję well_done () i nie będzie różnicy. Oddzwanianie przypomina weryfikację, weryfikacja jest sekwencją komunikatów, zwykle dwie, a wywołanie zwrotne jest zabezpieczeniem funkcji, zwykle dwóch. Jeśli przykleiłeś więcej niż 3 oddzwaniania, gratulacje, lubisz robić rzeczy po ciężku.
m3nda
16

Myślę, że lepszym przykładem użycia wywołań zwrotnych są funkcje asynchroniczne. Nie mogę mówić za Luą, ale w JavaScript jedną z najczęstszych akcji asynchronicznych jest kontaktowanie się z serwerem za pośrednictwem AJAX.

Wywołanie AJAX jest asynchroniczne, co oznacza, że ​​jeśli zrobisz coś takiego:

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

zawartość ifbloku nie będzie działać niezawodnie, a gdy tak się dzieje, dzieje się tak tylko dlatego, że ajaxCall()funkcja zakończyła się, zanim wykonanie dotarło do ifinstrukcji.

Jednak wywołania zwrotne eliminują ten problem, zapewniając zakończenie połączenia asynchronicznego przed wywołaniem wymaganej funkcji. Twój kod zmieni się na coś takiego:

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

ajaxCall(ajaxCallback);

Ma to na celu umożliwienie ci wykonywania takich czynności, jak zbieranie danych, bez ingerencji w inne rzeczy, takie jak rysowanie interfejsu. Jeśli gromadzenie danych było synchroniczne, interfejs przestałby reagować, gdy aplikacja czekała na pobranie danych. Brak odpowiedzi interfejsu jest bardzo niekorzystny dla wygody użytkownika, ponieważ większość użytkowników uzna, że ​​aplikacja „uległa awarii” i spróbuje przerwać proces.

Aby zobaczyć to w działaniu, wystarczy spojrzeć na każdą stronę internetową, która dokonuje dowolnego rodzaju aktualizacji bez ponownego ładowania całej strony. Dobrym przykładem są serwisy Twitter, LinkedIn i StackExchange. „Niekończące się przewijanie” kanałów Twittera i powiadomienia SE (zarówno dla powiadomień użytkowników, jak i powiadomień, że pytanie ma nową aktywność), są obsługiwane przez połączenia asynchroniczne. Kiedy widzisz gdzieś pokrętło, oznacza to, że sekcja wykonała asynchroniczne wywołanie i czeka na zakończenie tego połączenia, ale możesz wchodzić w interakcje z innymi rzeczami w interfejsie (a nawet wykonywać inne wywołania asynchroniczne). Po zakończeniu tego połączenia zareaguje i odpowiednio zaktualizuje interfejs.

Shauna
źródło
12

Zadałeś pytanie

Dlaczego potrzebujemy „funkcji oddzwaniania”?

Postaram się odpowiedzieć na to w zwięzły i jasny sposób (jeśli mi się nie uda, zapoznaj się z linkiem wiki na dole tej odpowiedzi).

Oddzwanianie służy do odroczenia konkretnej implementacji czegoś do ostatniej możliwej chwili.

Przykład przycisku jest dobrą ilustracją tego. Załóżmy, że chcesz mieć przycisk w aplikacji, który drukuje stronę na drukarce, możemy sobie wyobrazić świat, w którym w tym celu musiałbyś napisać całą nową PrinterButtonklasę.

Na szczęście mamy pojęcie wywołania zwrotnego (dziedziczenie i użycie wzorca szablonu jest również rodzajem semantycznego wywołania zwrotnego), więc nie musimy tego robić.

Zamiast ponownie wdrażać każdy aspekt przycisku, robimy to, aby dodać do implementacji przycisku. ButtonMa pojęcie robi coś , gdy jest wciśnięty. Po prostu mówimy, co to jest.

Ten mechanizm wstrzykiwania zachowań do frameworków nazywa się callbackami.

Przykład:

Wprowadźmy zachowanie do przycisku HTML:

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

W tym przypadku onclickwskazuje na wywołanie zwrotne, którym w tym przykładzie jest print()metoda.


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

AlexanderBrevig
źródło
Dzięki, po przeczytaniu twojej ilustracji, idę czytać wiki, których przykłady są tylko synchronicznymi wywołaniami zwrotnymi.
Lucas Li
powiedz funkcji odpowiedź za każdym razem, gdy ma problem i musisz słuchać problemów; nauczyć funkcję rozwiązania problemu, który sam się zajmuje.
Joe
8

Dlaczego potrzebujemy funkcji oddzwaniania? w jakich sytuacjach mogę to zastosować?

Funkcje oddzwaniania są bardzo przydatne w programowaniu sterowanym zdarzeniami. Pozwalają na skonfigurowanie programu w taki sposób, aby zdarzenia wyzwalały prawidłowy kod. Jest to bardzo powszechne w programach z GUI, w których użytkownicy mogą kliknąć dowolny element interfejsu użytkownika (np. Przyciski lub pozycje menu), a odpowiedni kod zostanie uruchomiony.

FrustratedWithFormsDesigner
źródło
1
+ Do tego są przydatne połączenia zwrotne, ale nie znoszę ich pisać :-)
Mike Dunlavey,
Kiedy byłem studentem pierwszego roku, mój nauczyciel nauczył nas programowania w MFC, ale nigdy nie powiedział nam, co to jest callback :-(
Lucas Li
2

Co dzieje się bez połączeń zwrotnych:

Facet 1: Dobra, czekam na to, co się stanie. (gwizdki, kciuki kręci)

Facet 2: Cholera! Dlaczego facet 1 nie pokazuje mi rzeczy? Chcę zobaczyć, jak dzieją się rzeczy !! Gdzie są moje rzeczy? Jak ktokolwiek ma tu coś zrobić?

Jim
źródło
1

Po pierwsze, termin callback odnosi się do zamknięcia, które jest używane do czegoś.

Załóżmy na przykład, że tworzysz funkcję zamknięcia i po prostu przechowujesz ją w zmiennej. To nie jest oddzwanianie, ponieważ nie jest używane do niczego.

Załóżmy jednak, że tworzysz zamknięcie i przechowujesz je w miejscu, w którym zostanie wywołane, gdy coś się wydarzy. To jest teraz nazywane oddzwonieniem.

Zwykle wywołania zwrotne są tworzone przez inne części programu niż części, które je wywołują. Łatwo więc sobie wyobrazić, że niektóre części programu „oddzwaniają” do innych części.

Mówiąc najprościej, wywołania zwrotne pozwalają jednej części programu powiedzieć innej części, aby coś zrobiła (cokolwiek), gdy coś się wydarzy.

Jeśli chodzi o zmienne utrzymywane przy życiu z powodu użycia ich w zamknięciu, jest to zupełnie inna funkcja zwana upvalues ​​(aka „przedłużająca żywotność zmiennych lokalnych” wśród tych, którzy nie mówią Lua). I to jest całkiem przydatne.

Jonathan Graef
źródło
Tak, Extending the lifetime of local variables to także określenie upvalue.
Lucas Li
Hmm interesujące. Termin „ aktualizacja” wydaje się być specyficzny dla Lua, chociaż szybkie wyszukiwanie w Google pokazuje, że czasami używano go do opisywania innych języków. Dodam to do mojej odpowiedzi.
Jonathan Graef
1

Oddzwanianie pojawiło się przynajmniej od pierwszych dni Fortran. Na przykład, jeśli masz solver ODE (zwykłe równanie różniczkowe), taki jak solver Runge-Kutta, może to wyglądać następująco:

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

Umożliwia dzwoniącemu dostosowanie zachowania podprogramu poprzez przekazanie mu funkcji specjalnego przeznaczenia, która może być wywoływana w razie potrzeby. Pozwala to na użycie solvera ODE do symulacji szerokiej gamy systemów.

Mike Dunlavey
źródło
1

Natknąłem się na to i chciałem dostarczyć bardziej aktualną odpowiedź ...

Funkcje zwrotne pozwalają nam zrobić coś z danymi w późniejszym czasie, pozwalając na uruchomienie reszty naszego kodu, zamiast czekać na to. Kod asynchroniczny pozwala nam na luksus wykonywania wszystkiego później . Najbardziej czytelnym przykładem funkcji oddzwaniania, na które natrafiłem, są obietnice w JavaScript. W poniższym przykładzie każdym razem, gdy widzisz funkcję (wynik) lub (newResult) lub (finalResult) ... są to funkcje zwrotne. Kod w nawiasach klamrowych jest wykonywany po powrocie danych z serwera. Tylko w tym momencie sensowne byłoby wykonywanie tych funkcji, ponieważ teraz potrzebne im dane są dostępne.

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

kod pochodzi z ... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

Mam nadzieję, że to komuś pomaga. To pomogło mi zrozumieć oddzwonienia :)

NRV
źródło
1
wydaje się, że nie oferuje to nic istotnego w porównaniu z punktami poczynionymi i wyjaśnionymi w poprzednich 9 odpowiedziach
gnat
1
Może dla ciebie, ale to właśnie sprawiło, że oddzwanianie było dla mnie jasne, więc pomyślałem, że mogę się podzielić. IMO sprawia, że ​​czytelność tego przykładu jest tego warta. Jestem pewien, że możemy zgodzić się na czytelność kodu, co jest bardzo ważne, szczególnie dla początkujących.
NRV,
-2

Nie znam lua, ale w ogólnych metodach wywoływania zwrotnego niektóre przypominają wielowątkowość. Na przykład w programowaniu aplikacji mobilnych większość aplikacji działa jak wysyłanie żądań do serwera i zabawa z interfejsem użytkownika z danymi pochodzącymi z odpowiedzi serwera. Gdy użytkownik wysyła żądanie do serwera, uzyskanie odpowiedzi z serwera zajmie trochę czasu, ale dla najlepszego interfejsu użytkownika UX nie powinien się blokować.

Z tego powodu używamy wielu wątków do wykonywania operacji równoległych. Gdy otrzymamy odpowiedź z serwera, musimy zaktualizować interfejs użytkownika. z tego wątku musimy powiadomić o aktualizacji. ze świata funkcjonalnego. Ten typ wywołań funkcji nazywa się metodami wywołania zwrotnego. Kiedy wywołasz te metody, kontroler powinien powrócić do głównego wątku. Na przykład metodami wywołania zwrotnego są bloki w celu C.

AMohan
źródło
tak, to co powiedziałeś jest zgodne z moim zrozumieniem po przeczytaniu książki.
Lucas Li
2
Metody oddzwaniania to nic innego jak wielowątkowość. Metody wywołania zwrotnego są po prostu wskaźnikami funkcji (delegaci). Wątki dotyczą przełączania / wykorzystania kontekstu procesora. Bardzo uproszczonym kontrastem byłoby to, że wątki dotyczą optymalizacji procesora dla UX, podczas gdy wywołania zwrotne dotyczą przełączania pamięci na polimorfizm. Po prostu dlatego, że wątki mogą wykonywać wywołania zwrotne, nie oznacza, że ​​wątki i wywołania zwrotne są podobne. Wątki wykonują również metody statyczne na klasach statycznych, ale typy wątków i statyczne nie są podobne.
rism