Навіщо нам потрібні «функції зворотного дзвінка»?


16

Я читаю книгу programming in Lua. Це сказало це

Закриття є цінним інструментом у багатьох контекстах. Як ми бачили, вони корисні як аргументи до функцій вищого порядку, таких як сортування. Закриття є цінним для функцій, які також будують інші функції, як наш приклад NewCounter; Цей механізм дозволяє програмам Lua включати складні методи програмування з функціонального світу. Закриття також корисне для функцій зворотного дзвінка. Типовий приклад тут виникає при створенні кнопок у звичайному наборі інструментів GUI. Кожна кнопка має функцію зворотного дзвінка, яку потрібно викликати, коли користувач натискає кнопку; ви хочете, щоб різні кнопки робили дещо різні речі при натисканні. Наприклад, цифровому калькулятору потрібно десять подібних кнопок, по одному на кожну цифру. Ви можете створити кожен з них такою функцією:

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

Здається, що якщо я зателефоную до digitButton, він поверне action(це створить закриття), тож я можу отримати доступ до digitпереданого digitButton.

Моє запитання таке:

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


Автор сказав:

У цьому прикладі ми припускаємо, що кнопка - це функція інструментарію, яка створює нові кнопки; label - мітка кнопки; і дія - це закриття зворотного дзвінка, яке потрібно викликати при натисканні кнопки. Зворотний виклик можна викликати довгий час після того, як digitButton зробив своє завдання і після того, як цифра локальної змінної вийшла за межі області, але вона все ще може отримати доступ до цієї змінної.

на думку автора, я думаю, що подібний приклад такий:

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

таким чином, the callback can be called a long time after digitButton did its task and after the local variable digit went out of scope.


Відповіді:


45

Хлопець 1 - Хлопець 2: ей чувак, я хочу щось робити, коли користувач натискає туди, передзвони мені, коли це станеться добре?

Хлопець 2 передзвонює Хлопець 1, коли користувач натискає тут.


1
Мені дуже подобається цей жарт з викликом :-P
Лукас Лі,

6
Це тільки я? Я не розумію, як це пояснює, чому нам потрібні функції зворотного дзвінка.
phant0m

2
Це більше схоже на те, що Хлопець 1 каже Хлопцеві 2 "коли настає час зробити це". Хлопець 2 йде про свій день, і коли настає час, це робить. Якщо ви мали на увазі Гая 1 для того, щоб викликати Гая 2, то це неправильно, оскільки Хлопець 2 не передзвонив Хлопця 1, він закликає робити те, що потрібно робити. Насправді, якщо Гай 1 викликає Гай 2, у вас за цим сценарієм буде важкий цикл. Якщо ви мали на увазі, що Хей 1 був зворотним дзвінком, то це неправильно, оскільки зворотний дзвінок не має уявлення, хто такий Гай 2, і, звичайно, не просить його дзвонити.
rism

Це жахливе пояснення.
всередині

21

Ні, це ніколи не поверне дію. Кнопка виконає її, коли користувач натисне на неї. І це відповідь. Вам потрібні функції зворотного виклику, коли ви хочете визначити дії, які повинні відбуватися в іншому компоненті у відповідь на подію, яку ви не контролюєте (цикл подій, який є частиною системи, виконає метод кнопки, який у свою чергу виконає дія).


Я не згоден з вами. Я відредагував своє повідомлення і додав кілька кодів. Я все ще думаю, що дія не виконується миттєво.
Лукас Лі

2
Тім, ти маєш рацію - але так і січень. "Кнопка виконає її, коли користувач натисне на неї", а не миттєво.
ОлександрБревіг

@AlexanderBrevig Так, я все ще думаю, що відповідь Яна дуже корисна для мене. Це дозволяє мені знати, що таке зворотній дзвінок і чому нам потрібен зворотний дзвінок.
Лукас Лі

Зворотний виклик - це просто брудне ім'я, яке хтось додав до другої функції, яка працює, коли перша звільняється і, можливо, ґрунтується на критеріях. Викличте свою функцію добре_done () і не матиме різниці. Зворотний дзвінок - це як перевірити, перевірити - це послідовність повідомлень, як правило, два, а зворотний дзвінок - це послідовність функцій, як правило, двох. Якщо ви прикували більше 3-х зворотних викликів, привітань, вам подобається робити речі важким способом.
м3нда

16

Я вважаю, що кращим прикладом для використання зворотних викликів є асинхронні функції. Я не можу говорити за Lua, але в JavaScript однією з найпоширеніших асинхронних дій є звернення до сервера через AJAX.

Виклик AJAX асинхронний, тобто якщо ви робите щось подібне:

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

вміст ifблоку не буде надійно запускатися правильно, і коли він буде, це лише тому, що ajaxCall()функція закінчилася до того, як виконання досягне ifзаяви.

Однак зворотні виклики усувають цю проблему, гарантуючи, що асинхронний виклик закінчений перед викликом потрібної функції. Отже, ваш код зміниться на щось подібне:

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

ajaxCall(ajaxCallback);

Мета цього - дозволити вам робити такі речі, як збір даних, не втручаючись в інші речі, наприклад малювання інтерфейсу. Якби збір даних був синхронним, інтерфейс став би невідповідним, оскільки програма чекала отримання даних. Інтерфейс, що не відповідає, дуже поганий для користувачів, тому що більшість користувачів вважають, що додаток "вийшов з ладу" і спробують припинити процес.

Щоб побачити це в дії, вам просто потрібно переглянути будь-який веб-сайт, який робить будь-яке оновлення, не перезавантажуючи всю сторінку. Twitter, LinkedIn та сайти StackExchange - хороші приклади. "Нескінченна прокрутка" каналів та сповіщення SE (як для сповіщень користувачів, так і для повідомлень про те, що питання має нову активність), забезпечуються асинхронними дзвінками. Коли ви десь бачите спінер, це означає, що розділ здійснив асинхронний дзвінок і чекає, коли цей виклик завершиться, але ви можете взаємодіяти з іншими речами в інтерфейсі (і навіть робити інші асинхронні дзвінки). Після того, як цей виклик закінчиться, він реагуватиме та оновить інтерфейс відповідно.


12

Ви поставили запитання

Навіщо нам потрібні "функції зворотного дзвінка"?

Я спробую відповісти на це лаконічно і чітко (якщо мені це не вдасться, будь ласка, зверніться до посилання wiki внизу цієї відповіді).

Відклики викликів використовуються для відкладання конкретної реалізації чогось до останнього можливого моменту.

Приклад кнопки - хороша ілюстрація цього. Скажімо, ви хочете, щоб у вашій програмі була кнопка, яка друкує сторінку на принтері, ми можемо уявити собі світ, де для цього вам доведеться кодувати цілий новий PrinterButtonклас.

На щастя, у нас є поняття зворотних викликів (успадкування та використання шаблону шаблону - також своєрідний семантичний зворотний виклик), тому нам не потрібно цього робити.

Замість того, щоб повторно реалізовувати кожен аспект кнопки, те, що ми робимо, - це додати до реалізації кнопки. Концепція Buttonмає щось робити при натисканні. Ми просто говоримо йому, що це таке.

Цей механізм введення поведінки в рамки називається зворотним викликом.

Приклад:

Давайте введемо деяку поведінку в кнопку HTML:

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

У цьому випадку onclickвказує на зворотний виклик, який для цього прикладу є print()методом.


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


Дякую, прочитавши вашу ілюстрацію, я йду читати вікі, приклади яких - лише синхронні зворотні дзвінки.
Лукас Лі

повідомляйте функцію відповідь щоразу, коли виникає проблема, і ви повинні слухати проблеми; навчити функції розв’язувати задачу, вона піклується про себе.
Джо

8

Чому нам потрібні функції зворотного дзвінка? до яких ситуацій я можу це застосувати?

Функції зворотного дзвінка дуже корисні в програмуванні на основі подій. Вони дозволяють налаштувати свою програму таким чином, щоб події викликали правильний код. Це дуже часто зустрічається в програмах з графічним інтерфейсом, де користувачі можуть натиснути на будь-який елемент інтерфейсу користувача (наприклад, кнопки або пункти меню), і відповідний код буде запущений.


1
+ Ось для чого гарні зворотні виклики, але мені неприємно писати їх :-)
Майк Данлаве

Коли я був лише першокурсником, мій вчитель навчав нас програмувати в MFC, але він ніколи не сказав нам, що таке зворотній зв'язок :-(
Лукас Лі

2

Що відбувається без зворотних викликів:

Хлопець 1: Гаразд, я чекаю, коли це станеться. (свисти, щебетати великі пальці)

Хлопець 2: Чорт! Чому Хлопець 1 не показує мені речі? Я хочу бачити, що відбувається !! Де мої речі? Як хтось повинен щось тут зробити?


1

Фірстофф, термін зворотного виклику відноситься до закриття, яке використовується для чогось.

Наприклад, припустимо, що ви створили функцію закриття і просто зберігаєте її у змінній. Це не зворотний дзвінок, оскільки він не використовується ні для чого.

Але, припустимо, ви створите закриття і зберігаєте його десь, щоб воно зателефонувало, коли щось трапиться. Зараз це називається зворотним дзвоном.

Зазвичай зворотні дзвінки створюються різними частинами програми, ніж частинами, які їх викликають. Тому легко уявити, що деякі частини програми "передзвонюють" інші частини.

Простіше кажучи, зворотні дзвінки дозволяють одній частині програми сказати іншій частині зробити щось (що-небудь), коли щось відбувається.

Що стосується збереження змінних завдяки використанню в закритті, то це зовсім інша функція, яка називається upvalues ​​(також "продовження терміну служби локальних змінних" серед тих, хто не говорить на Lua). І це теж досить корисно.


так, Extending the lifetime of local variablesтакож слід сказати термін upvalue.
Лукас Лі

Хм, цікаво. Термін "підвищена вартість", схоже, є специфічним для Lua, хоча швидкий пошук в Google показує, що він час від часу використовується для опису інших мов. Я додам це до своєї відповіді.
Джонатан Грейф

1

Зворотні дзвінки існували, принаймні, з перших днів Фортран. Наприклад, якщо у вас є вирішувач ODE (звичайне диференціальне рівняння), такий як розв'язувач Runge-Kutta, це може виглядати приблизно так:

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

Це дає можливість абоненту налаштувати поведінку підпрограми, передаючи їй функцію спеціального призначення, яку потрібно викликати, коли це необхідно. Це дозволяє використовувати вирішувач ODE для імітації найрізноманітніших систем.


1

Натрапили на це і хотіли дати більш актуальну відповідь ...

Функції зворотного дзвінка дозволяють нам робити щось із даними пізніше , дозволяючи решта нашого коду працювати, замість того, щоб чекати. Асинхронний код дозволяє нам розкішно виконувати що-небудь пізніше . Найбільш читаним прикладом функцій зворотного дзвінка, які я натрапив, є Обіцяння в JavaScript. У прикладі нижче щоразу, коли ви бачите функцію (результат) або (newResult) або (finalResult) ... це функції зворотного виклику. Код у їх фігурних дужках виконується після повернення даних із сервера. Тільки в цей момент було б доцільно виконувати ці функції, оскільки тепер необхідні дані є.

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

код взято з ... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

Сподіваємось, це комусь допомагає. Саме це допомогло мені зрозуміти зворотні дзвінки :)


1
це, здається, не пропонує нічого суттєвого щодо питань, викладених та пояснених у попередніх 9 відповідях
gnat

1
Можливо, для вас, але це те, що насправді зрозуміло для мене зворотній зв'язок, тому я думав, що поділюсь. IMO читабельність прикладу - це те, що робить його вартим. Я впевнений, що ми можемо погодитись з читабельністю коду далеко, особливо для початківців.
NRV

-2

Я не знаю Луа, але взагалі методи зворотного виклику, які схожі на багатопотоковість. Наприклад, у програмуванні розробки мобільних додатків більшість програм функціонує як надсилання запиту на сервер та гра з користувальницьким інтерфейсом з даними, отриманими як відповідь від сервера. Коли користувач надсилає запит на сервер, знадобиться час, щоб отримати відповідь від сервера, але для кращого UX UI не повинен застрявати.

Через це ми використовуємо кілька потоків для паралельних операцій. Коли ми отримуємо відповідь від сервера, нам потрібно оновити інтерфейс користувача. ми повинні повідомити з цього потоку, щоб оновити. з функціонального світу. Цей тип функціональних викликів називається методами зворотного виклику. Коли ви викликаєте ці методи, контролер повинен повернутися до основного потоку. Наприклад, методи зворотного виклику - це блоки в Objective-C.


так, те, що ви сказали, узгоджується з моїм розумінням після прочитання книги.
Лукас Лі

2
Методи зворотного виклику - це не що інше, як багатопотокове. Методи зворотного виклику - це просто функціональні покажчики (делегати). Нитки стосуються перемикання / використання контексту процесора. Сильно спрощеним контрастом було б те, що нарізка стосується оптимізації процесора для UX, тоді як зворотні виклики стосуються перемикання пам'яті на поліморфізм. Просто те, що потоки можуть виконувати зворотні дзвінки, не означає, що потоки та зворотні виклики схожі. Нитки також виконують статичні методи на статичних класах, але нитки та статичні типи не схожі.
rism
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.