Що таке функція зворотного дзвінка?


684

Що таке функція зворотного дзвінка?


8
ви можете знайти тут: stackoverflow.com/a/9652434/3343174 найкраще пояснення щодо зворотних викликів
Фахер


Подивіться другу відповідь для детального пояснення
Донато

Найкраще пояснення зворотного дзвінка, який я коли-небудь знаходив youtube.com/watch?v=xHneyv38Jro
Sameer Sinha

Відповіді:


681

Розробники часто плутають те, що таке зворотній дзвінок через назву проклятої речі.

Функція зворотного дзвінка - це функція, яка:

  • доступний іншою функцією, і
  • викликається після першої функції, якщо ця перша функція завершена

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

Можливо, кращою назвою була б функція "дзвінок після" .

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

Псевдокод:

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

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

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

Результат, якщо ви викликали event ():

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

Тут важливий порядок виводу. Оскільки функції зворотного дзвінка викликаються після цього, "Я закінчив друк номерів" друкується останнім, а не першим.

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

Деякі мови підтримують конструкції, де підтримуються кілька аргументів функції зворотного виклику, і викликаються на основі того, як завершується батьківська функція (тобто один зворотний виклик викликається у випадку успішного завершення батьківської функції, інший викликається у випадку, якщо батьківська функція передає a конкретна помилка тощо).


31
Ваш приклад чудовий, але я не розумію, чому термінологія "зворотний дзвінок". Коли означаєOfLife "передзвонився"?
CodyBugstein

4
Привіт, о once its parent method completes, the function which this argument represents is then called. Отже, якщо функція передається іншій функції як аргумент, але викликається з середини виконуваної функції, як parent(cb) {dostuff1(); cb(); dostuff2()}тоді, вона не вважається callbackфункцією?
Макс Ярі

2
@MaxYari: IMHO, це все ще вважається зворотним викликом. Тут важливим є те, що батьківський функтин збирається якось використовувати функцію введення (aka callback). Його можна назвати посередині чи наприкінці, або якщо умова виконана.
Kamran Bigdely

12
@ 8bitjunkie спасибі - але де в методі printANumber викликається метод smisOfLife?
BKSpurgeon

2
Це зовсім не відповідає дійсності: "автоматично викликається після завершення першої функції". Зворотний виклик не повинен виконуватися взагалі, не кажучи вже про автоматичне. Насправді, не рідкість завершення зворотних дзвінків до завершення батьківської функції. Мені дуже не подобається, як люди описують зворотні дзвінки як функції, які виконуються "пізніше". Це дуже заплутано для людей, які дізнаються про них. Простіше кажучи, зворотні виклики - це лише функції, передані як аргументи іншим функціям. Період. Краще пояснення буде включати пояснення ЧОМУ зворотних викликів над посиланнями на функції.
Йорданія

225

Непрозоре визначення

Функція зворотного дзвінка - це функція, яку ви надаєте іншому фрагменту коду, що дозволяє йому викликати цей код.

Надуманий приклад

Чому б ти хотів це зробити? Скажімо, є послуга, яку потрібно викликати. Якщо послуга повертається негайно, ви просто:

  1. Назви це
  2. Зачекайте результату
  3. Продовжуйте, як тільки результат прийде

Наприклад, припустимо, що послуга була factorialфункцією. Коли ви хочете значення 5!, ви б викликалиfactorial(5) , і наступні кроки відбудуться:

  1. Ваше поточне місце виконання збережено (у стеці, але це не важливо)

  2. Виконання передано о factorial

  3. Коли factorial завершується, він поміщає результат де - то ви можете отримати до нього

  4. Виконання повертається туди, де воно було в [1]

Тепер припустимо factorial зайняло дуже багато часу, тому що ви даєте йому величезну кількість і йому потрібно десь запуститись на якомусь суперкомп'ютерному кластері. Скажімо, ви очікуєте, що для отримання результату знадобиться 5 хвилин. Ви можете:

  1. Тримайте дизайн і запускайте програму вночі, коли ви спите, щоб не дивитись на екран половину часу

  2. Створіть свою програму, щоб вона робила інші речі, поки factorialробить це

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

Повний дизайн

Щоб використовувати шаблон зворотного виклику, потрібно зателефонувати factorialнаступним чином:

factorial(really_big_number, what_to_do_with_the_result)

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

Так, це означає, що factorialпотрібно писати для підтримки зворотних дзвінків.

Тепер припустимо, що ви хочете мати можливість передавати параметр до зворотного дзвінка. Тепер ви не можете, тому що ви не збираєтесь його називати, так factorial. Тому factorialпотрібно написати, щоб ви могли передати свої параметри, і він просто передасть їх вашому зворотному дзвінку, коли він викликає його. Це може виглядати приблизно так:

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

Тепер, що factorialдозволяє цю модель, ваш зворотний дзвінок може виглядати приблизно так:

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

і ваш заклик був factorialби

factorial(42, logIt, logger)

Що робити, якщо ви хочете щось повернути logIt? Ну, ви не можете, бо factorialне звертаєте на це уваги.

Ну чому ж не можна factorialпросто повернути те, що повертає ваш зворотний дзвінок?

Зробити це не блокуючим

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

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

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

І до речі, використовуючи цю схему factorial_worker_task можна запустити зворотний дзвінок асинхронно і повернутися негайно.

Так, що ти робиш?

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

a = f()
g(a)

і fщоб вас називали асинхронно, ви замість цього будете писати

f(g)

де gпередається як зворотний дзвінок.

Це принципово змінює поточну топологію вашої програми і вимагає певного звикання.

Ваша мова програмування може вам дуже допомогти, даючи вам змогу створювати функції на ходу. У коді, що знаходиться вище, функція gможе бути такою ж маленькою print (2*a+1). Якщо ваша мова вимагає, щоб ви визначили це як окрему функцію, з абсолютно непотрібним іменем та підписом, то ваше життя стане неприємним, якщо ви багато використовуєте цей зразок.

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

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

що набагато приємніше.

Як передати зворотний дзвінок

Як би ви передали функцію зворотного дзвінка factorial? Ну, ви могли це зробити різними способами.

  1. Якщо викликана функція працює в тому ж процесі, ви можете передати вказівник функції

  2. Або, можливо, ви хочете підтримувати словник fn name --> fn ptrу своїй програмі, і в цьому випадку ви можете передати ім'я

  3. Можливо, ваша мова дозволяє вам визначити функцію на місці, можливо, як лямбда! Всередині він створює якийсь об’єкт і передає вказівник, але вам не доведеться турбуватися про це.

  4. Можливо, функція, яку ви викликаєте, працює на цілком окремій машині, і ви викликаєте її за допомогою мережевого протоколу типу HTTP. Ви можете виставити зворотний дзвінок як функцію дзвінка HTTP та передати його URL.

Ви отримуєте ідею.

Нещодавній підйом зворотних дзвінків

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

Але ми не можемо очікувати, що наші програми будуть заблоковані, поки ми чекаємо, коли ці служби реагують. Усвідомлюючи це, постачальники послуг часто проектують API, використовуючи шаблон зворотного виклику.

JavaScript дуже добре підтримує зворотні дзвінки, наприклад, з лямбдами та закриттями. І в світі JavaScript є багато активності, як у браузері, так і на сервері. Існують навіть платформи JavaScript, що розробляються для мобільних пристроїв.

По мірі просування вперед все більше і більше з нас будуть писати асинхронний код, для якого це розуміння буде важливим.


1
Концепція дуже добре пояснена ..! :)
piyushGoyal

Чудове пояснення, +1.
Lingamurthy CS

1
Це найкраща відповідь серед усіх інших. Ласкаво підтверджуйте це.
Абхішек Налін

3
Пояснив ідеально і все пояснено. Мені б хотілося, щоб я знову його схвалив.
Йогеш Ядав

Так, я розумію, як працюють лямби в javascript та ruby. І java 8, але старі версії java не використовували лямб, а натомість використовували класи, і я хотів би знати, як працює такий тип зворотного дзвінка. Все-таки чудова відповідь на іншу.
Donato

97

Зауважте, що зворотний дзвінок - це одне слово.

Сторінка зворотного виклику у Вікіпедії пояснює це дуже добре.

цитата зі сторінки вікіпедії:

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


14
Гарний спосіб представити відповідь.
Чатуранга Чандрасекара

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

22
Це міг бути коментар - в основному це посилання на Вікіпедію
CodyBugstein

У Вікіпедії насправді є деякі справді дивовижні програми програмування. Мені завжди здавалося, що термін "зворотний виклик" найкраще пояснюється за допомогою фрази "Я збираюся передзвонити до ..."
Томас

Чудове пояснення на javascriptissexy.com/…; що я буду тут репостувати; Функція зворотного дзвінка - це функція, яка передається іншій функції як параметр, а функція зворотного виклику викликається або виконується всередині іншогоFunction. // Зауважте, що елемент у параметрі методу клацання - це функція, а не змінна. // Елемент є функцією зворотного виклику $ ("# btn_1"). Click (function () {alert ("Btn 1 Clicked" );}); Як ви бачите в попередньому прикладі, ми передаємо функцію в якості параметра методу клацання для її виконання -
MarcoZen

46

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


42

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

Зазвичай він використовується при запуску завдання, яке закінчиться асинхронно (тобто закінчиться через деякий час після повернення функції виклику).

Наприклад, функція запиту веб-сторінки може вимагати від її абонента надати функцію зворотного дзвінка, яка буде викликана після завершення завантаження веб-сторінки.


У своєму першому реченні ви говорите, "...when a condition is met"але я думав, що зворотні виклики викликаються, коли батьківська функція закінчує виконання і не залежить від умов (?).
Ojonugwa Jude Ochalifu

"Певна умова" просто означає, що вони, як правило, викликаються з причини, а не випадково. Зворотний виклик може бути викликаний, коли батько / творець все ще виконує - це може призвести до стану гонки, якщо програміст цього не очікує.
Томас Братт

Гаразд. Дякую за уточнення
Ojonugwa Jude Ochalifu

34

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

- Пол Якубік, "Реалізація зворотних дзвінків у C ++"


1
Отже, моє ім’я та номер - це функція?
Корай Тугай

Ні, це була б аналогія, якби "зворотний дзвінок" був хорошим ім'ям того, що натомість: ви попросите телефонного оператора здійснити дзвінок. Кінець.
gherson

33

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

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

Я думаю, що люди просто читають перше речення визначення wiki:

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

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

Насправді лише друге речення у визначенні wiki виявляє відмінності між функцією зворотного виклику та нормальною функцією:

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

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

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


2
Ваша відповідь має сенс ... але я маю проблеми з її зображенням. Чи можете ви навести приклад?
CodyBugstein

3
@Zane Wong :: В останньому ви написали "Визначення говорить про те, що ваша передана функція буде називатися функцією" нижчого рівня "." Чи можете ви поясніть, що вказує на функцію нижчого рівня? Краще, якщо навести приклад.
Віку

Приклад був би приємний
Yousuf Azad

1
Я думаю, що різниця між класичним стилем виклику функції та стилем зворотного дзвінка полягає у зв’язку із залежним напрямком: якщо модуль A залежить від ("використовує") модуля B, A викликає функцію B, це не зворотний виклик. Якщо A передає посилання на свою функцію B, тоді B викликає функцію A, це зворотний виклик: виклик йде назад, порівняно із залежністю модуля.
XouDo

30

Давайте будемо просто. Що таке функція зворотного дзвінка?

Приклад за притчею та аналогією

У мене є секретар. Щодня я прошу її: (i) скинути вихідну пошту фірми на пошту, і після того, як вона це зробить, зробити: (ii) будь-яке завдання, яке я написав для неї на одному з таких наліпок .

Тепер, яке завдання на паличці? Завдання варіюється з дня на день.

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

Підсумовуючи:

  1. по-перше, їй потрібно скинути пошту і
  2. одразу після цього вона повинна роздрукувати деякі документи.

Функція зворотного дзвінка - це друге завдання: друк цих документів. Тому що це робиться ПІСЛЯ пошти, яку випадають, а також тому, що клейка записка, яка сповіщає її надрукувати документ, надається їй разом з поштою, яку їй потрібно опублікувати.

Давайте тепер зв’яжемо це з лексикою програмування

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

Ось і все. Нічого більше. Я сподіваюся, що це зрозуміло для вас - а якщо ні, напишіть коментар, і я зроблю все можливе, щоб уточнити.


18

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

Я не впевнений, що вони є.

Я думаю, що зворотні виклики - це фактично виклик функції, як наслідок виклику та завершення іншої функції.

Я також думаю, що зворотні звороти мають на меті вирішувати виклик, що походить, таким чином, "ей! Те, що ви просили?


1
+1 для опитування зворотних зворотів проти повернення заяв. Мене звикли цим і так багато випускників, з якими я працюю.
8bitjunkie

2
Гарна відповідь - допоміг мені зрозуміти це на відміну від багатьох інших відповідей!
адаам

18

Що таке зворотний дзвінок ?

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

Що таке функція зворотного дзвінка ?

  • функція зворотного дзвінка - це як Слуга, який "передзвонив" своєму господареві, коли він виконав завдання.
  • функція зворотного виклику є функцією , яка передається в іншу функцію (назвемо цю іншу функцію otherFunction) в якості параметра, а функція зворотного виклику викликається (або виконується) всередині otherFunction.
    function action(x, y, callback) {
        return callback(x, y);
    }

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

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

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

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

У SOA зворотний виклик дозволяє модулям плагінів отримувати доступ до служб з контейнера / середовища.

Аналогія: зворотні дзвінки. Асинхронний. Неблокуючий
приклад реального життя для зворотного дзвінка


Функція зворотного дзвінка сама по собі не є функцією вищого порядку. Він передається функції вищого порядку.
danio

17

Call After було б кращим іменем, ніж дурне ім'я, зворотний дзвінок . Коли або, якщо умова досягається в межах функції, зателефонуйте на іншу функцію - функцію « Після виклику» , яку отримано як аргумент.

Замість того, щоб жорсткий код внутрішньої функції всередині функції, він пише функцію, щоб прийняти вже написану функцію Call After як аргумент. Виклику Після того, як може викликатися на основі змін стану детектується коду в функції приймаючої аргумент.


Це чудова ідея. Я пішов на "подзвонив ззаду", щоб спробувати і пояснити це. Я міг бачити когось на кшталт Мартіна Фаулера, що популяризує "дзвінок після", як новий термін для них у своєму семінарському блозі.
8bitjunkie

15

Функція зворотного виклику - це функція, яку ви вказуєте на існуючу функцію / метод, яку потрібно викликати після завершення дії, вимагає додаткової обробки тощо.

Наприклад, у Javascript або конкретніше jQuery можна вказати аргумент зворотного виклику, який потрібно викликати, коли анімація закінчена.

У PHP ця preg_replace_callback()функція дозволяє надати функцію, яка буде викликана, коли регулярний вираз збігається, передаючи рядки (и), відповідні як аргументи.


10

дивіться на зображення :)ось як це працює

Основна функція бібліотеки викликів програми (яка може бути також функцією системного рівня) з назвою функції зворотного виклику. Ця функція зворотного виклику може бути реалізована в декілька способів. Основна програма вибирає один зворотний дзвінок відповідно до вимог.

Нарешті, функція бібліотеки викликає функцію зворотного дзвінка під час виконання.


7
Ви не хотіли б також додати до цього текстове пояснення? Якщо образ зникає, ця відповідь втрачає весь контекст.
Тім Пост

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

З усіх давно окрилених описів, які я бачив тут, цей - той, який змусив мене піти "ааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааа (ауа" Майте нагороду.
DiBosco

7

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


7

Припустимо, у нас є функція, sort(int *arraytobesorted,void (*algorithmchosen)(void))де вона може сприймати покажчик функції як свій аргумент, який може бути використаний у певний момент sort()реалізації. Потім тут код, на який звертається вказівник функції algorithmchosen, називається функцією зворотного виклику .

І бачити перевагу в тому, що ми можемо вибрати будь-який алгоритм, наприклад:

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

Які були, скажімо, реалізовані з прототипом:

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

Це концепція, що використовується для досягнення поліморфізму в об'єктно-орієнтованому програмуванні


Чудове пояснення на javascriptissexy.com/…; що я буду тут репостувати; Функція зворотного дзвінка - це функція, яка передається іншій функції як параметр, а функція зворотного виклику викликається або виконується всередині іншогоFunction. // Зауважте, що елемент у параметрі методу клацання - це функція, а не змінна. // Елемент є функцією зворотного виклику $ ("# btn_1"). Click (function () {alert ("Btn 1 Clicked" );}); Як ви бачите в попередньому прикладі, ми передаємо функцію як параметр методу натискання для його виконання -
MarcoZen

4

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

Зворотний виклик в C за допомогою функціонального вказівника

У C зворотний виклик реалізується за допомогою функціонального покажчика. Функція Покажчик - як випливає з назви, є вказівником на функцію.

Наприклад, int (* ptrFunc) ();

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

Ось якийсь код для демонстрації функції вказівника.

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

Тепер спробуємо розібратися в понятті зворотного виклику в C, використовуючи функцію вказівника.

Повна програма має три файли: callback.c, reg_callback.h та 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)();                               
}

Якщо ми запустимо цю програму, вихід буде

Це програма, що демонструє зворотний виклик функцій всередині register_callback всередині my_callback назад всередині основної програми

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

Зворотний виклик в Java за допомогою інтерфейсу

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

Дозвольте продемонструвати це на прикладі:

Інтерфейс зворотного виклику

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

Клас абонента або клас вищого рівня

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

Функція Callee або нижній шар

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

Зворотний виклик за допомогою шаблону EventListener

  • Елемент списку

Цей шаблон використовується для сповіщення від 0 до n номерів спостерігачів / слухачів про те, що конкретне завдання закінчено

  • Елемент списку

Різниця між механізмом зворотного виклику та механізмом EventListener / Observer полягає в тому, що при зворотному виклику виклик сповіщає одного абонента, тоді як у Eventlisener / Observer виклик може повідомити всіх, хто зацікавлений у цій події (сповіщення може переходити до деяких інших частин додаток, який не запустив завдання)

Дозвольте пояснити це на прикладі.

Інтерфейс події

public interface Events {

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

Віджет класу

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

Кнопка класу

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

Класифікація

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

Клас діяльності

пакет 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();
    }   
}

Інший клас

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

Основний клас

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

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

Ці два інтерфейси відповідають за прослуховування подій, які можуть статися на компонентах інтерфейсу, похідних віджетів, таких як кнопка або прапорець. Тож якщо ми порівняємо цей приклад із попереднім прикладом зворотного виклику за допомогою інтерфейсу Java, ці два інтерфейси працюють як інтерфейс зворотного виклику. Отже, код вищого рівня (Here Activity) реалізує ці два інтерфейси. І щоразу, коли подія трапляється з віджетом, буде викликатися код вищого рівня (або метод цих інтерфейсів, реалізований у коді вищого рівня, який є тут Активністю).

Тепер дозвольте мені обговорити основну різницю між шаблоном зворотного виклику та Eventlistener. Як ми вже згадували, що, використовуючи зворотний виклик, Клієнт може повідомити лише одного абонента. Але у випадку з шаблоном EventListener будь-яка інша частина або клас Додатку може зареєструвати події, які можуть статися на кнопці або прапорці. Прикладом такого роду класів є OtherClass. Якщо ви побачите код OtherClass, ви виявите, що він зареєструвався як слухач ClickEvent, який може виникнути в кнопці, визначеній у діяльності. Цікавою є те, що, окрім діяльності (абонента), цей OtherClass також буде повідомлений, коли подія клацання відбувається на кнопці.


Будь ласка, уникайте лише відповідей на посилання . Відповіді, які є "ледь більше, ніж посилання на зовнішній сайт", можуть бути видалені .
Квентін,

3

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

  • повідомлення про закінчення завдання
  • запит порівняння між двома елементами (як у c qsort ())
  • звітування про хід процесу
  • повідомлення про події
  • делегування інстанції об’єкта
  • делегування картини місцевості

...

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


2

Зворотний виклик - це ідея передати функцію як параметр іншій функції та викликати її після завершення процесу.

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

"Що змусило їх (комп'ютерних вчених) розвинути зворотній зв'язок?" Ви можете дізнатися проблему, яка блокує. (Особливо блокування інтерфейсу користувача) І зворотний виклик - не єдине рішення. Існує маса інших рішень (наприклад, нитка, майбутнє, обіцянки ...).


1

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


1

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

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

Тут ми передаємо функцію як параметр методу клацання. І метод клацання викликає (або виконує) функцію зворотного виклику, яку ми перейшли до неї.


1
Функція зворотного дзвінка сама по собі не є функцією вищого порядку. Він передається функції вищого порядку.
danio

1

Функція зворотного дзвінка Функція, яка перейшла до іншої функції як аргумент.

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

setTimeout(test_function, 2000);

Примітка. У наведеному вище прикладі test_function використовується як аргумент для функції setTimeout.


1
Ласкаво просимо до переповнення стека! Перш ніж відповісти на запитання, завжди прочитайте наявні відповіді. Цю відповідь уже надано. Замість того, щоб повторювати відповідь, проголосуйте за існуючу відповідь. Деякі вказівки щодо написання гарних відповідей можна знайти тут .
dferenc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.