Что такое функция обратного вызова?


685

Что такое функция обратного вызова?


8
Вы можете найти здесь: stackoverflow.com/a/9652434/3343174 лучшее объяснение о обратных
вызовах


Посмотрите на второй ответ для подробного объяснения
Донато

Лучшее объяснение обратного вызова, которое я когда-либо нашел youtube.com/watch?v=xHneyv38Jro
Самер Синха

Ответы:


683

Разработчиков часто смущает, что такое обратный вызов из-за названия проклятой вещи.

Функция обратного вызова - это функция, которая:

  • доступ к другой функции, и
  • вызывается после первой функции, если эта первая функция завершается

Хороший способ представить, как работает функция обратного вызова, состоит в том, что это функция, которая «вызывается сзади » функции, в которую она передается.

Может быть, лучшим именем будет функция «вызов после» .

Эта конструкция очень полезна для асинхронного поведения, когда мы хотим, чтобы действие происходило всякий раз, когда завершается предыдущее событие.

псевдокод:

// 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.

Порядок вывода здесь важен. Поскольку функции обратного вызова вызываются позже, «Я закончил печатать номера» печатается последним, а не первым.

Обратные вызовы являются так называемыми из-за их использования с языками указателей. Если вы не используете один из них, не работайте над именем «обратный вызов». Просто поймите, что это просто имя для описания метода, который передается в качестве аргумента другому методу, например, когда вызывается родительский метод (любое условие, например, нажатие кнопки, отметка таймера и т. Д.) И его тело метода завершается, затем вызывается функция обратного вызова.

Некоторые языки поддерживают конструкции, в которых поддерживаются несколько аргументов функции обратного вызова, и вызываются на основе завершения родительской функции (т. Е. Один обратный вызов вызывается в случае успешного завершения родительской функции, другой вызывается в случае, если родительская функция выбрасывает конкретная ошибка и т. д.).


31
Ваш пример великолепен, но я не понимаю, почему термин "обратный вызов". Когда смысл «жизни» «перезвонил»?
CodyBugstein

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

2
@MaxYari: ИМХО, он все еще считается обратным вызовом. Здесь важно то, что родительский functin каким-то образом будет использовать функцию ввода (иначе callback). Это может быть вызвано в середине или в конце, или если условие выполнено.
Кямран Бигдели

12
@ 8bitjunkie, спасибо - но где метод valueOfLife вызывается в функции printANumber?
BKSpurgeon

2
Это совсем не так: «автоматически вызывается после завершения первой функции». Обратный вызов не должен выполняться вообще, не говоря уже о автоматическом. Фактически, нередки случаи, когда обратные вызовы завершаются до завершения родительской функции. Мне очень не нравится, как люди описывают обратные вызовы как функции, которые выполняются «позже». Это очень сбивает с толку людей, которые узнают о них. Проще говоря, обратные вызовы - это просто функции, которые передаются в качестве аргументов другим функциям. Период. Лучшее объяснение будет включать в себя объяснение, почему обратные вызовы по ссылкам на функции.
Иордания

226

Непрозрачное определение

Функция обратного вызова - это функция, которую вы предоставляете другому фрагменту кода, что позволяет ему вызываться этим кодом.

Придуманный пример

Зачем тебе это делать? Допустим, есть служба, которую нужно вызвать. Если сервис возвращается сразу, вы просто:

  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
Концепция очень хорошо объяснена ..! :)
ПиюшГоял

Отличное объяснение +1.
Lingamurthy CS

1
Это лучший ответ среди всех остальных. Пожалуйста, поддержите это.
Абхишек Налин

3
Объяснил отлично и все объяснил. Хотел бы я повторить это снова.
Йогеш Ядав

Да, я понимаю, как работают lambas в javascript и ruby. И java 8, но старые версии java не использовали lambas и вместо этого использовали классы, и я хотел бы знать, как работает этот тип обратного вызова. Все еще лучший ответ на другой.
Донато

97

Обратите внимание, что обратный вызов - это одно слово.

Страница обратного вызова Википедии объясняет это очень хорошо.

цитата со страницы википедии:

В компьютерном программировании обратный вызов - это ссылка на исполняемый код или фрагмент исполняемого кода, который передается в качестве аргумента другому коду. Это позволяет программному уровню более низкого уровня вызывать подпрограмму (или функцию), определенную на уровне более высокого уровня.


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

1
И это также приводит к ответу по-другому. Существительное «обратный вызов» - это то, что было «перезвонено», точно так же, как то, что прошло через завершение работы, было закрыто, а то, что используется для входа в систему, является логином.
Аноним

22
Это мог бы быть комментарий - в основном это ссылка на Википедию
CodyBugstein

В Википедии действительно есть кое-что действительно интересное в программировании. Я всегда чувствовал, что термин «обратный вызов» лучше всего объяснить с помощью фразы «Я собираюсь перезвонить ...»
Томас

Отличное объяснение на javascriptissexy.com/…; который я буду размещать здесь; Функция обратного вызова - это функция, которая передается другой функции в качестве параметра, а функция обратного вызова вызывается или выполняется внутри другой функции. // Обратите внимание, что элемент в параметре метода click является функцией, а не переменной. // Элемент является функцией обратного вызова $ ("# btn_1"). Click (function () {alert ("Btn 1 Clicked" );}); Как вы видите в предыдущем примере, мы передаем функцию в качестве параметра методу click для ее выполнения -
MarcoZen

46

Непрофессионал ответил бы, что это функция, которая вызывается не вами, а пользователем или браузером после определенного события или после обработки некоторого кода.


42

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

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

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


В первом предложении вы говорите, "...when a condition is met"но я думал, что обратные вызовы вызываются, когда родительская функция завершает выполнение и не зависит от условий (?).
Оджонугва Джуд Очалифу

«Определенное условие» просто означает, что их обычно вызывают по причине, а не по случайности. Обратный вызов может быть вызван, когда родитель / создатель все еще выполняется - это может привести к состоянию гонки, если программист этого не ожидает.
Томас Братт

Хорошо. Спасибо за разъяснения
Оджонугва Джуд Очалифу

34

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

- Пол Якубик, "Реализации обратного вызова в C ++"


1
Значит, мое имя и номер - это функция?
Корай Тугай

Нет, это было бы аналогией, если бы «обратный вызов» был хорошим названием для того, что вместо этого: вы просите телефонного оператора сделать звонок. Конец.
Херсон

33

Я полагаю, что этот «обратный вызов» жаргон был ошибочно использован во многих местах. Мое определение будет что-то вроде:

Функция обратного вызова - это функция, которую вы передаете кому-то и позволяете ему вызывать ее в определенный момент времени.

Я думаю, что люди просто читают первое предложение определения вики:

обратный вызов - это ссылка на исполняемый код или фрагмент исполняемого кода, который передается в качестве аргумента другому коду.

Я работал со многими API, вижу множество плохих примеров. Многие люди склонны называть указатель на функцию (ссылка на исполняемый код) или анонимные функции (часть исполняемого кода) «обратным вызовом», если они просто функции, зачем вам другое имя для этого?

На самом деле, только второе предложение в определении вики раскрывает различия между функцией обратного вызова и нормальной функцией:

Это позволяет программному уровню более низкого уровня вызывать подпрограмму (или функцию), определенную на уровне более высокого уровня.

так что разница в том, кому вы собираетесь передать функцию и как будет вызываться ваша переданная функция. Если вы просто определяете функцию, передаете ее другой функции и вызываете ее непосредственно в теле этой функции, не вызывайте ее как обратный вызов. В определении сказано, что переданная вами функция будет вызываться функцией «нижнего уровня».

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


2
Ваш ответ имеет смысл ... но у меня возникли проблемы с его изображением. Можете привести пример?
CodyBugstein

3
@Zane Wong :: В последнем написанном вами «Определение говорит, что ваша переданная функция будет вызываться функцией« более низкого уровня ».» Не могли бы вы объяснить, что обозначает функция нижнего уровня? Лучше, если вы приведете пример.
Вику

Пример был бы хорош
Юсуф Азад

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
Хороший ответ - помог мне понять это в отличие от многих других ответов!
Adaam

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 обратный вызов позволяет подключаемым модулям получать доступ к сервисам из контейнера / среды.

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


Функция обратного вызова сама по себе не является функцией высшего порядка. Он передается функции высшего порядка.
Данио

17

Call After будет лучшим именем, чем глупое имя, callback . Когда или если условие выполняется внутри функции, вызовите другую функцию, функцию Call After , полученную в качестве аргумента.

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


Это отличная идея. Я пошел на «позвонил в спину», чтобы попытаться объяснить это. Я мог видеть кого-то вроде Мартина Фаулера, популяризирующего «призыв после», как новый термин для них в его оригинальном блоге.
8bitjunkie

15

Функция обратного вызова - это функция, которую вы указываете для существующей функции / метода, которая вызывается, когда действие завершено, требует дополнительной обработки и т. Д.

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

В PHP preg_replace_callback()функция позволяет предоставить функцию, которая будет вызываться при сопоставлении регулярного выражения, передавая строку (и), совпадающую в качестве аргументов.


10

посмотрите на изображение :)вот как это работает

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

Наконец, библиотечная функция вызывает функцию обратного вызова во время выполнения.


7
Не могли бы вы добавить к этому текстовое объяснение? Если изображение исчезает, этот ответ теряет весь контекст.
Тим Пост

текст от других людей объясняет это лучше всего. единственное, что мне не хватало, так это изображение :)

Из всех длинных описаний, которые я видел здесь, именно это заставило меня сказать: «Аааа, теперь я вижу его использование». Имейте upvote.
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/…; который я буду размещать здесь; Функция обратного вызова - это функция, которая передается другой функции в качестве параметра, а функция обратного вызова вызывается или выполняется внутри другой функции. // Обратите внимание, что элемент в параметре метода click является функцией, а не переменной. // Элемент является функцией обратного вызова $ ("# btn_1"). Click (function () {alert ("Btn 1 Clicked" );}); Как вы видите в предыдущем примере, мы передаем функцию в качестве параметра методу click для ее выполнения -
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;
}

Теперь давайте попробуем понять концепцию Callback в 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);
}
}

Как видно из приведенного выше кода, у нас есть интерфейс с названием events, который в основном перечисляет все события, которые могут произойти для нашего приложения. Класс Widget является базовым классом для всех компонентов пользовательского интерфейса, таких как Button, Checkbox. Эти компоненты пользовательского интерфейса являются объектами, которые фактически получают события из кода платформы. Класс Widget реализует интерфейс Events, а также имеет два вложенных интерфейса: OnClickEventListener и OnLongClickEventListener.

Эти два интерфейса отвечают за прослушивание событий, которые могут произойти в компонентах пользовательского интерфейса, производных от виджетов, таких как Button или Checkbox. Поэтому, если мы сравним этот пример с более ранним примером Callback с использованием интерфейса Java, эти два интерфейса будут работать как интерфейс Callback. Таким образом, код более высокого уровня (Здесь Activity) реализует эти два интерфейса. И всякий раз, когда событие происходит с виджетом, будет вызываться код более высокого уровня (или метод этих интерфейсов, реализованный в коде более высокого уровня, который здесь называется Activity).

Теперь позвольте мне обсудить основные различия между шаблонами Callback и Eventlistener. Как мы уже упоминали, используя Callback, Callee может уведомить только одного абонента. Но в случае шаблона EventListener любая другая часть или класс приложения может регистрироваться для событий, которые могут происходить на кнопке или флажке. Примером такого класса является класс OtherClass. Если вы увидите код OtherClass, вы обнаружите, что он зарегистрировал себя в качестве прослушивателя ClickEvent, что может происходить в кнопке, определенной в Activity. Интересно, что помимо Activity (Вызывающий объект), этот OtherClass также будет уведомляться всякий раз, когда происходит событие нажатия кнопки.


Пожалуйста, избегайте ссылки только ответы . Ответы, которые «едва ли больше, чем ссылка на внешний сайт», могут быть удалены .
Квентин,

3

Функция обратного вызова - это функция, которую вы передаете (в качестве ссылки или указателя) определенной функции или объекту. Эта функция или объект будет вызывать эту функцию обратно в любое время, возможно, несколько раз, для любых целей:

  • уведомление о завершении задачи
  • запрос сравнения между двумя элементами (как в c qsort ())
  • отчетность о ходе процесса
  • уведомляющие события
  • делегирование экземпляра объекта
  • делегирование картины области

...

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


2

Обратный вызов - это идея передать функцию в качестве параметра другой функции и вызвать ее после завершения процесса.

Если вы получили концепцию обратного вызова через удивительные ответы выше, я рекомендую вам изучить основы его идеи.

«Что заставило их (компьютерных ученых) разработать обратный вызов?» Вы можете изучить проблему, которая блокирует (особенно блокирует пользовательский интерфейс), и обратный вызов не является единственным решением. Есть много других решений (например: Thread, Futures, Promises ...).


1

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


1

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

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

Здесь мы передаем функцию в качестве параметра методу click. И метод click вызовет (или выполнит) функцию обратного вызова, которую мы передали ему.


2
Функция обратного вызова сама по себе не является функцией высшего порядка. Он передается функции высшего порядка.
Данио

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.