Как объяснить обратные вызовы на простом английском языке? Как они отличаются от вызова одной функции от другой функции?

Как объяснить обратные вызовы на простом английском языке? Как они отличаются от вызова одной функции из другой функции, принимая какой-то контекст от вызывающей функции? Как объяснить их власть начинающему программисту?

Ответ 1

Часто приложение должно выполнять разные функции на основе своего контекста/состояния. Для этого мы используем переменную, в которой мы будем хранить информацию о вызываемой функции. В соответствии с его потребностью приложение установит эту переменную с информацией о вызываемой функции и вызовет функцию с использованием той же переменной.

В javascript ниже приведен пример. Здесь мы используем аргумент метода как переменную, где храним информацию о функции.

function processArray(arr, callback) {
    var resultArr = new Array(); 
    for (var i = arr.length-1; i >= 0; i--)
        resultArr[i] = callback(arr[i]);
    return resultArr;
}

var arr = [1, 2, 3, 4];
var arrReturned = processArray(arr, function(arg) {return arg * -1;});
// arrReturned would be [-1, -2, -3, -4]

Ответ 2

Я собираюсь попытаться сохранить это мертвым просто. "Обратный вызов" - это любая функция, вызываемая другой функцией, которая принимает первую функцию в качестве параметра. Много раз "обратный вызов" - это функция, которая вызывается, когда что-то происходит. Это что-то можно назвать "событием" в программировании.

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

Вы могли бы сказать им, чтобы получить пакет и донести его до соседей. Если ваш супруг был таким же глупым, как компьютер, они сидели бы у двери и дожидались пакета до тех пор, пока он не придет (НЕ ДЕЛАЯ ЧТО-НИБУДЬ), а затем, когда он придет, они передадут его соседям. Но есть лучший способ. Скажите своему супругу, что, когда они получают пакет, они должны принести его соседям. Затем они могут нормально жить, пока они не получат пакет.

В нашем примере получение пакета - это "событие", а его приведение к соседям - это "обратный вызов". Ваш супруг "запускает" ваши инструкции, чтобы передать пакет только после того, как пакет поступит. Гораздо лучше!

Такое мышление очевидно в повседневной жизни, но компьютеры не имеют такого же здравого смысла. Подумайте, как обычно программисты записывают файл:

fileObject = open(file)
# now that we have WAITED for the file to open, we can write to it
fileObject.write("We are writing to the file.")
# now we can continue doing the other, totally unrelated things our program does

Здесь мы WAIT для открытия файла, прежде чем мы напишем ему. Это "блокирует" поток исполнения, и наша программа не может выполнять какие-либо другие действия, которые это может потребоваться! Что делать, если мы могли бы сделать это вместо:

# we pass writeToFile (A CALLBACK FUNCTION!) to the open function
fileObject = open(file, writeToFile)
# execution continues flowing -- we don't wait for the file to be opened
# ONCE the file is opened we write to it, but while we wait WE CAN DO OTHER THINGS!

Оказывается, мы делаем это с некоторыми языками и фреймворками. Это довольно круто! Проверьте Node.js, чтобы получить реальную практику с таким мышлением.

Ответ 3

Как объяснить обратные вызовы на простом английском языке?

На простом английском языке функция обратного вызова похожа на Рабочий, который "обращается" к своему Менеджменту, когда он выполнил Задача.

Как они отличаются от вызова одной функции от другой функции взяв какой-то контекст из вызывающей функции?

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

Как их способность объяснять начинающему программисту?

Сила обратных вызовов можно легко увидеть на сайтах в стиле AJAX, которые должны извлекать данные с сервера. Загрузка новых данных может занять некоторое время. Без обратных вызовов весь пользовательский интерфейс "зависает" при загрузке новых данных, или вам нужно будет обновить всю страницу, а не только ее часть. С обратным вызовом вы можете вставить изображение "теперь загружать" и заменить его новыми данными после его загрузки.

Некоторый код без обратного вызова:

function grabAndFreeze() {
    showNowLoading(true);
    var jsondata = getData('http://yourserver.com/data/messages.json');
    /* User Interface 'freezes' while getting data */
    processData(jsondata);
    showNowLoading(false);
    do_other_stuff(); // not called until data fully downloaded
}

function processData(jsondata) { // do something with the data
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

С обратным вызовом:

Вот пример с обратным вызовом, используя jQuery getJSON:

function processDataCB(jsondata) { // callback: update UI with results
   showNowLoading(false);
   var count = jsondata.results ? jsondata.results.length : 0;
   $('#counter_messages').text(['Fetched', count, 'new items'].join(' '));
   $('#results_messages').html(jsondata.results || '(no new messages)');
}

function grabAndGo() { // and don't freeze
    showNowLoading(true);
    $('#results_messages').html(now_loading_image);
    $.getJSON("http://yourserver.com/data/messages.json", processDataCB);
    /* Call processDataCB when data is downloaded, no frozen User Interface! */
    do_other_stuff(); // called immediately
}

С закрытием:

Часто обратный вызов должен получить доступ к state от вызывающей функции с помощью closure, который похож на Рабочий, требующий получить информацию от Менеджера, прежде чем он может выполнить свою Задачу. Чтобы создать closure, вы можете встроить функцию, чтобы она просматривала данные в вызывающем контексте:

/* Grab messages, chat users, etc by changing dtable. Run callback cb when done.*/
function grab(dtable, cb) { 
    if (null == dtable) { dtable = "messages"; }
    var uiElem = "_" + dtable;
    showNowLoading(true, dtable);
    $('#results' + uiElem).html(now_loading_image);
    $.getJSON("http://yourserver.com/user/"+dtable+".json", cb || function (jsondata) {
       // Using a closure: can "see" dtable argument and uiElem variables above.
       var count = jsondata.results ? jsondata.results.length : 0, 
           counterMsg = ['Fetched', count, 'new', dtable].join(' '),
           // no new chatters/messages/etc
           defaultResultsMsg = ['(no new ', dtable, ')'].join(''); 
       showNowLoading(false, dtable);
       $('#counter' + uiElem).text(counterMsg);
       $('#results'+ uiElem).html(jsondata.results || defaultResultsMsg);
    });
    /* User Interface calls cb when data is downloaded */

    do_other_stuff(); // called immediately
}

Использование:

// update results_chatters when chatters.json data is downloaded:
grab("chatters"); 
// update results_messages when messages.json data is downloaded
grab("messages"); 
// call myCallback(jsondata) when "history.json" data is loaded:
grab("history", myCallback); 

Закрытие

Наконец, вот определение closure из Дуглас Крокфорд:

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

См. также:

Ответ 4

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

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

customizableFunc(customFunctionality)

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

    customizableFucn(customFunctionality) {
      var data = doSomthing();
      customFunctionality(data);
      ...
    }

Хотя такого рода внедренные функции часто называют "обратным вызовом", в этом нет ничего конъюнктивного. Очень очевидным примером является метод forEach, в котором настраиваемая функция предоставляется в качестве аргумента для применения к каждому элементу в массиве для изменения массива.

Но это принципиально отличается от использования функций "обратного вызова" для асинхронного программирования, как в AJAX или node.js, или просто для назначения функциональности событиям взаимодействия с пользователем (например, щелчкам мыши), В этом случае вся идея состоит в том, чтобы дождаться события контингента перед выполнением пользовательских функций. Это очевидно в случае взаимодействия с пользователем, но также важно в процессах ввода/вывода (ввода/вывода), которые могут занять время, например, чтение файлов с диска. Именно здесь термин "обратный вызов" имеет наиболее очевидный смысл. Как только процесс ввода-вывода запускается (например, запрашивает чтение файла с диска или сервера для возврата данных из HTTP-запроса), асинхронная программа не ждет, когда она закончится. Он может выполнять любые задачи, запланированные на следующий день, и отвечать только с помощью пользовательских функций после того, как он будет уведомлен о завершении чтения файла или HTTP-запроса (или что это не удалось) и что данные доступны для пользовательских функций. Это, как позвонить в бизнес по телефону и оставить свой номер "обратного вызова" , чтобы они могли позвонить вам, когда кто-то сможет вернуться к вам. Это лучше, чем повешение на линии, для того, кто знает, как долго и не может заниматься другими делами.

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

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

Ответ 5

В терминах, отличных от программиста, обратный вызов является заполнением в пробе в программе.

Общим предметом многих бумажных форм является "Человек, который следует звонить в случае чрезвычайной ситуации". Там есть пустая строка. Вы пишете имя и номер телефона. Если возникает чрезвычайная ситуация, то этот человек получает вызов.

  • Каждый получает ту же пустую форму, но
  • Каждый может написать другой номер экстренного контакта.

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

Пример 1:

Обратные вызовы используются как настраиваемые методы, возможно, для добавления/изменения поведения программы. Например, возьмите некоторый C-код, который выполняет функцию, но не знает, как печатать выходные данные. Все, что он может сделать, это сделать строку. Когда он пытается понять, что делать со строкой, он видит пустую строку. Но программист дал вам пробел, чтобы написать обратный вызов!

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

  • Чистая переменная в модуле/коде является пустой строкой,
  • set_print_callback - это пучок,
  • и the_callback - ваша информация, которую вы заполняете.

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

Пример 2:

Когда вам сообщают, что вам нужно позвонить в номер экстренной службы, вы заходите и читаете то, что написано на бумажной форме, а затем вызывайте номер, который вы читаете. Если эта строка пуста, ничего не будет сделано.

Программирование Gui работает так же. Когда нажимается кнопка, программа должна выяснить, что делать дальше. Он идет и ищет обратный вызов. Этот обратный вызов находится в пробеле с надписью "Вот что вы делаете при нажатии кнопки Button"

Большинство IDE автоматически заполнит пробел для вас (напишите базовый метод), когда вы попросите его (например, button1_clicked). Однако этот пробел может иметь любой метод, который вы хорошо проглотите. Вы можете вызвать метод run_computations или butter_the_biscuits, если вы поместите это имя обратного вызова в правильный пробел. Вы можете поместить "555-555-1212" в номер экстренной службы пустым. Это не имеет большого смысла, но это допустимо.


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

Ответ 6

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

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

Ответ 7

Всегда лучше начинать с примера:).

Предположим, что у вас есть два модуля A и B.

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

Таким образом, все B теперь нужно делать, это "обратный вызов" в модуль A, когда определенное событие/условие происходит с помощью указателя функции. A может выполнять дополнительную обработку внутри функции обратного вызова.

*) Явное преимущество здесь заключается в том, что вы абстрагируете все, что касается модуля A от модуля B. Модуль B не должен заботиться о том, кто/какой модуль A.

Ответ 8

Представьте, что вам нужна функция, которая возвращает квадрат в 10, чтобы написать функцию:

function tenSquared() {return 10*10;}

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

function nineSquared() {return 9*9;}

В конце концов вы замените все их на общую функцию:

function square(x) {return x*x;}

Точное мышление применяется для обратных вызовов. У вас есть функция, которая что-то делает, и когда делаются вызовы doA:

function computeA(){
    ...
    doA(result);
}

Позже вы хотите, чтобы одна и та же функция вызывала doB, вместо этого вы могли дублировать всю функцию:

function computeB(){
    ...
    doB(result);
}

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

function compute(callback){
    ...
    callback(result);
}

Тогда вам просто нужно вызвать compute (doA) и вычислить (doB).

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

Ответ 9

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

Ответ 10

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

Аналогия ожидания прибытия посылки, которая использовалась другими ответами, является хорошей для объяснения того и другого. В компьютерной программе вы сказали бы, что компьютер ожидает посылку. Обычно он сидел бы там и ждал (и ничего не делал) до тех пор, пока посылка не поступит, возможно, бесконечно, если она не придет. Для людей это звучит глупо, но без дальнейших мер это совершенно естественно для компьютера.

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

Ответ 11

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

В Python, например,

grabDBValue( (lambda x: passValueToGUIWindow(x) ))

grabDBValue может быть записано только для получения значения из базы данных, а затем позволяет указать, что на самом деле делать со значением, поэтому оно принимает функцию. Вы не знаете, когда или если grabDBValue вернется, но если/когда это произойдет, вы знаете, что хотите. Здесь я передаю анонимную функцию (или лямбду), которая отправляет значение в окно графического интерфейса. Я мог бы легко изменить поведение программы, выполнив следующие действия:

grabDBValue( (lambda x: passToLogger(x) ))

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

Protip:

В языках с лексическим охватом (например, Scheme или Perl) вы можете сделать такой трюк:

my $var = 2;
my $val = someCallerBackFunction(sub callback { return $var * 3; });
# Perlistas note: I know the sub doesn't need a name, this is for illustration

$val в этом случае будет 6, потому что обратный вызов имеет доступ к переменным, объявленным в лексической среде, где он был определен. Лексический охват и анонимные обратные вызовы - это мощная комбинация, которая требует дальнейшего изучения для начинающего программиста.

Ответ 12

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

Альтернативным методом является запуск этого кода параллельно и продолжение вашей работы. Но что, если ваш исходный код должен делать разные вещи в зависимости от ответа от кода, который он назвал? Ну, в этом случае вы можете передать имя/местоположение кода, который вы хотите вызвать, когда это будет сделано. Это "обратный вызов".

Обычный код: Запросить информацию- > Информация о процессе- > Обработать результаты обработки- > Продолжать делать другие вещи.

С обратными вызовами: Запросить информацию- > Информация о процессе- > Продолжать делать другие вещи. И в какой-то более поздний момент → Поделитесь результатами обработки.

Ответ 13

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

Итак, если нам нужно обеспечить реальное динамическое поведение для программы, мы можем использовать обратный вызов. С обратным вызовом вы можете инструктировать по параметрам, программе для вызова другой программы, предоставляющей некоторые ранее определенные параметры, и может ожидать некоторые результаты (это подпись контракта или операции), поэтому эти результаты могут быть получены/обработаны сторонней программой, которая wasn Ранее известный.

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

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

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

Я надеюсь, что это объяснение может быть полезно.

Ответ 14

Предположите, что вы должны дать мне потенциально долгосрочную задачу: получите имена первых пяти уникальных людей, с которыми вы сталкиваетесь. Это может занять несколько дней, если я нахожусь в малонаселенном районе. Вы не очень заинтересованы в том, чтобы сидеть на руках, пока я бегаю, поэтому вы говорите: "Когда у вас есть список, позвоните мне в свою камеру и прочитайте его мне. Вот номер".

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

В JavaScript это может выглядеть примерно так:

var lottoNumbers = [];
var callback = function(theNames) {
  for (var i=0; i<theNames.length; i++) {
    lottoNumbers.push(theNames[i].length);
  }
};

db.executeQuery("SELECT name " +
                "FROM tblEveryOneInTheWholeWorld " +
                "ORDER BY proximity DESC " +
                "LIMIT 5", callback);

while (lottoNumbers.length < 5) {
  playGolf();
}
playLotto(lottoNumbers);

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

Ответ 15

Обратный вызов - это функция, которая будет вызываться второй функцией. Эта вторая функция не знает заранее, какую функцию она будет вызывать. Таким образом, идентификатор функции обратного вызова хранится где-то или передается второй функции в качестве параметра. Этот "идентификатор", в зависимости от языка программирования, может быть адресом обратного вызова или каким-либо другим указателем, или это может быть имя функции. Принцип тот же, мы храним или передаем некоторую информацию, которая однозначно идентифицирует функцию.

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

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

Кстати, в зависимости от языка программирования слово "функция" в приведенном выше обсуждении может быть заменено на "блок", "закрытие", "лямбда" и т.д.

Ответ 16

Обратные вызовы легче всего описываются с точки зрения телефонной системы. Вызов функции аналогичен вызову кого-то по телефону, задавая ей вопрос, получая ответ и подвешивая; добавление обратного вызова изменяет аналогию, поэтому, задав ей вопрос, вы также дадите ей свое имя и номер, чтобы она могла перезвонить вам с ответом. - Пол Якубик , "Репликация обратного вызова в С++"

Ответ 17

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

function1(var1, var2) является обычным способом.

Что делать, если я хочу, чтобы var2 обрабатывался, а затем отправлялся в качестве аргумента? function1(var1, function2(var2))

Это один тип обратного вызова - где function2 выполняет некоторый код и возвращает переменную обратно к исходной функции.

Ответ 18

Метафорическое объяснение:

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

Поэтому я беру посылку на почту и прошу их доставить ее. Если я хочу знать, когда мой друг получает посылку, у меня есть два варианта:

(a) Я могу ждать в почтовом отделении до его доставки.

(b) Я получаю электронное письмо, когда оно будет доставлено.

Опция (b) аналогична обратному вызову.

Ответ 19

Для обучения обратным вызовам сначала необходимо учить указатель. Как только ученики поймут идею указателя на переменную, идея обратных вызовов станет проще. Предполагая, что вы используете C/С++, эти шаги могут быть выполнены.

  • Сначала покажите своим ученикам, как использовать и манипулировать переменными с помощью указателей, используя обычные идентификаторы переменных.
  • Затем научите их тому, что можно сделать только с указателями (например, передавая переменную по ссылке).
  • Затем расскажите им, как исполняемый код или функции похожи на некоторые другие данные (или переменные) в памяти. Таким образом, функции также имеют адреса или указатели.
  • Затем покажите им, как можно вызвать функции с указателями функций и сказать, что они называются обратными вызовами.
  • Теперь, вопрос в том, почему все эти хлопоты для вызова некоторых функций? Какая польза? Подобно указателям данных, указатель функции aka callbacks имеет некоторые преимущества перед использованием обычных идентификаторов.
  • Первый, идентификаторы функций или имена функций не могут использоваться как обычные данные. Я имею в виду, вы не можете создать структуру данных с функциями (например, с массивом или связанным списком функций). Но с обратными вызовами вы можете создать массив, связанный список или использовать их с другими данными, например, в словах пар ключ-значение или деревья или любыми другими вещами. Это мощная выгода. И другие преимущества на самом деле являются дочерними из этого.
  • Наиболее частое использование обратных вызовов наблюдается при программировании драйвера событий. Если одна или несколько функций выполняются на основе некоторого входящего сигнала. С обратными вызовами может поддерживаться словарь для отображения сигналов с обратными вызовами. Тогда разрешение входного сигнала и выполнение соответствующего кода станут намного проще.
  • Второе использование обратных вызовов, которые мне нужны, - это функции более высокого порядка. Функции, которые принимают другие функции в качестве входных аргументов. И для отправки функций в качестве аргументов нам нужны обратные вызовы. Примером может быть функция, которая принимает массив и обратный вызов. Затем он выполняет обратный вызов по каждому элементу массива и возвращает результаты в другом массиве. Если мы передадим функции удвоение обратного вызова, мы получим двузначный массив. Если мы пройдем квадратичный обратный вызов, мы получим квадраты. Для квадратных корней просто отправьте соответствующий обратный вызов. Это невозможно сделать с помощью обычных функций.

Там может быть много чего. Привлекайте учеников, и они обнаружат. Надеюсь, это поможет.

Ответ 20

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

  • Проверяйте окно каждые 5 минут, чтобы увидеть, не вышел ли Джо.
  • Продолжайте делать свое дело, пока Джо не зазвонит дверной звонок.

Вариант 1: Это больше похоже на пример опроса, в котором Джейн застряла бы в "цикле", проверяя, находится ли Джо вне. В то же время Джейн ничего не может сделать.

Вариант 2: Это пример обратного вызова. Джейн говорит Джо позвонить ей в дверь, когда он выходит наружу. Она дает ему "функцию", чтобы позвонить в дверной звонок. Джо не должен знать, как работает дверной звонок или где он находится, ему просто нужно позвонить в эту функцию, то есть позвонить в дверь, когда он там.

Обратные вызовы управляются "событиями". В этом примере "событие" - это прибытие Джо. В Ajax, например, события могут быть "успехом" или "сбоем" асинхронного запроса, и каждый из них может иметь одинаковые или разные обратные вызовы.

В отношении приложений JavaScript и обратных вызовов. Нам также необходимо понять "закрытие" и контекст приложения. Что означает "this", может легко запутать разработчиков JavaScript. В этом примере внутри каждого метода "ring_the_door_bell()" ring_the_door_bell() "могут быть некоторые другие методы, которые каждый человек должен делать в соответствии с их утренней рутиной ex." выключить телевизор() ". Мы хотели бы, чтобы "this" ссылался на объект" Джейн "или объект" Давид", чтобы каждый мог настроить все, что им нужно, прежде чем Джо подберет их. Именно здесь для настройки обратного вызова с Джо требуется пародирование метода, чтобы "this" ссылался на правильный объект.

Надеюсь, что это поможет!

Ответ 21

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

Ответ 22

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

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

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

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

Или вы можете создать каталог функций, передать список имен функций и запросить библиотеку для обратного вызова соответственно. "Успех обратного вызова(), если успех, callback fail(), если не удалось".

Давайте рассмотрим простой пример указателя функции

void cbfunc()
{
     printf("called");
}

 int main ()
 {
                   /* function pointer */ 
      void (*callback)(void); 
                   /* point to your callback function */ 
      callback=(void *)cbfunc; 
                   /* perform callback */
      callback();
      return 0; 
}

Как передать аргумент функции обратного вызова?

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

typedef struct myst
{
     int a;
     char b[10];
}myst;

void cbfunc(myst *mt) 
{
     fprintf(stdout,"called %d %s.",mt->a,mt->b); 
}

int main() 
{
       /* func pointer */
    void (*callback)(void *);       //param
     myst m;
     m.a=10;
     strcpy(m.b,"123");       
     callback = (void*)cbfunc;    /* point to callback function */
     callback(&m);                /* perform callback and pass in the param */
     return 0;   
}

Ответ 23

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

Пример "реального мира" - это локальный магазин видеоигр. Вы ждёте Half-Life 3. Вместо того, чтобы каждый день ходить в магазин, чтобы узнать, находится ли он, вы регистрируете свою электронную почту в списке, чтобы получать уведомление, когда игра доступна. Электронная почта становится вашим "обратным вызовом", и условие, которое необходимо выполнить, - это доступность игры.

Пример "программистов" - это веб-страница, где вы хотите выполнить действие при нажатии кнопки. Вы регистрируете метод обратного вызова для кнопки и продолжаете выполнение других задач. Когда/если пользователь нажимает кнопку, браузер будет смотреть список обратных вызовов для этого события и вызывать ваш метод.

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

Ответ 24

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

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

Ответ 25

Я думаю, что это довольно простая задача для объяснения.

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

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

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

Ответ 26

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

Обратный вызов в C с использованием указателя функции

В C обратный вызов реализуется с помощью Function Pointer. Указатель функций - как следует из названия, является указателем на функцию.

Например, 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 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);
}

Caller или класс более высокого уровня

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

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

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

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

Ответ 27

[отредактировано], когда у нас есть две функции, скажем functionA и functionB, если functionA зависит от functionB.

то мы называем functionB функцией обратного вызова. Это широко используется в рамках Spring.

callback function wikipedia example

Ответ 28

Подумайте о методе, который дает задание коллеге. Простой задачей может быть следующее:

Solve these equations:
x + 2 = y
2 * x = 3 * y

Ваш коллега усердно выполняет математику и дает вам следующий результат:

x = -6
y = -4

Но у вашей коллеги есть проблема, он не всегда понимает нотации, например ^, но он их понимает по их описанию. Например, exponent. Каждый раз, когда он находит одно из них, вы возвращаете следующее:

I don't understand "^"

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

Вы думаете о решении, вы просто добавляете следующее ко всем своим инструкциям:

If you have any questions about symbols, call me at extension 1234 and I will tell you its name.

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

Ответ 29

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

Меньше hardcode = проще поддерживать и изменять = меньше времени = больше бизнес-ценности = удивительность.

Например, в javascript, используя Underscore.js, вы можете найти все четные элементы в таком массиве:

var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]

Пример любезно предоставлен Underscore.js: http://documentcloud.github.com/underscore/#filter

Ответ 30

Это с точки зрения загрузки веб-страницы:

Ваша программа работает на мобильном телефоне и запрашивает веб-страницу http://www.google.com. Если вы пишете свою программу синхронно, функция, которую вы пишете для загрузки данных, будет работать непрерывно, пока не будут загружены все данные. Это означает, что ваш пользовательский интерфейс не будет обновляться и в основном будет заморожен. Если вы пишете свою программу с помощью обратных вызовов, вы запрашиваете данные и говорите: "Выполняйте эту функцию, когда закончите". Это позволяет пользовательскому интерфейсу по-прежнему разрешать взаимодействие пользователя во время загрузки файла. После завершения загрузки веб-страницы вызывается функция результата (обратный вызов) и вы можете обрабатывать данные.

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