Jump to content

Обратный вызов (компьютерное программирование)

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

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

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

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

Аналогия

[ редактировать ]

Чтобы помочь понять концепцию, приведем аналогию из реальной жизни.

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

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

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

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

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

Использовать

[ редактировать ]

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

Обработка событий

[ редактировать ]

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

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

Асинхронное действие

[ редактировать ]

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

Полиморфизм

[ редактировать ]

Обратный вызов может использоваться для реализации полиморфизма . В следующем псевдокоде SayHi могу взять либо WriteStatus или WriteError.

def WriteStatus(string message):
    Write(stdout, message)
def WriteError(string message):
    Write(stderr, message)
def SayHi(write):
    write("Hello world")

Условное действие

[ редактировать ]

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

def Log(getMessage):
    if isLoggingEnabled:
        message = getMessage();
        WriteLine(message);

Выполнение

[ редактировать ]

Технология обратного вызова реализуется по-разному в разных языках программирования .

В ассемблере , C , C++ , Pascal , Modula2 и других языках функция обратного вызова хранится внутри как указатель на функцию . Использование одного и того же хранилища позволяет различным языкам напрямую совместно использовать обратные вызовы без во время разработки или выполнения взаимодействия уровня . Например, Windows API доступен через несколько языков, компиляторов и ассемблеров.

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

Многие динамические языки , такие как JavaScript , Lua , Python , Perl. [1] [2] и PHP позволяют передавать объект функции.

Языки CLI , такие как C# и VB.NET, предоставляют типобезопасную ссылку на инкапсулирующую функцию, известную как делегат .

События и обработчики событий , используемые в языках .NET, обеспечивают обратные вызовы.

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

Многие языки, включая Perl, Python, Ruby , Smalltalk , C++ (11+), C# и VB.NET (новые версии), а также большинство функциональных языков, поддерживают лямбда-выражения , безымянные функции со встроенным синтаксисом, которые обычно действуют как обратные вызовы.

В некоторых языках, включая Scheme , ML , JavaScript, Perl, Python, Smalltalk, PHP (начиная с версии 5.3.0), [3] C++ (11+), Java (с 8), [4] и многих других, лямбда может быть замыканием , т. е. может обращаться к переменным, локально определенным в контексте, в котором определена лямбда.

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

В PL/I и ALGOL 60 процедура обратного вызова может нуждаться в возможности доступа к локальным переменным в содержащихся блоках, поэтому она вызывается через входную переменную, содержащую как точку входа, так и контекстную информацию. [5]

Пример кода

[ редактировать ]

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

В следующем коде C функция PrintNumber использует параметр getNumber как блокирующий обратный вызов. PrintNumber вызывается с GetAnswerToMostImportantQuestion который действует как функция обратного вызова. При запуске выводится: «Значение: 42».

#include <stdio.h>
#include <stdlib.h>

void PrintNumber(int (*getNumber)(void)) {
    int val = getNumber();
    printf("Value: %d\n", val);
}

int GetAnswerToMostImportantQuestion(void) {
    return 42;
}

int main(void) {
    PrintNumber(GetAnswerToMostImportantQuestion);
    return 0;
}

В C++ функтор в дополнение к указателю на функцию можно использовать .

В следующем C# коде метод Helper.Method использует параметр callback как блокирующий обратный вызов. Helper.Method вызывается с Log который действует как функция обратного вызова. При запуске в консоль пишет следующее: «Обратный вызов: Hello world».

public class MainClass
{
    static void Main(string[] args)
    {
        Helper helper = new Helper();
        helper.Method(Log);
    }

    static void Log(string str)
    {
        Console.WriteLine($"Callback was: {str}");
    }
}

public class Helper
{
    public void Method(Action<string> callback)
    {
        callback("Hello world");
    }
}

В следующем коде Kotlin функция askAndAnswer использует параметр getAnswer как блокирующий обратный вызов. askAndAnswer вызывается с getAnswerToMostImportantQuestion который действует как функция обратного вызова. Запустив это, пользователь узнает, что ответ на его вопрос — «42».

fun main() {
    print("Enter the most important question: ")
    val question = readLine()
    askAndAnswer(question, ::getAnswerToMostImportantQuestion)
}

fun getAnswerToMostImportantQuestion(): Int {
    return 42
}

fun askAndAnswer(question: String?, getAnswer: () -> Int) {
    println("Question: $question")
    println("Answer: ${getAnswer()}")
}

В следующем коде JavaScript функция calculate использует параметр operate как блокирующий обратный вызов. calculate вызывается с multiply а потом с sum которые действуют как функции обратного вызова.

function calculate(a, b, operate) {
    return operate(a, b);
}
function multiply(a, b) {
    return a * b;
}
function sum(a, b) {
    return a + b;
}
// outputs 20
alert(calculate(10, 2, multiply));
// outputs 12
alert(calculate(10, 2, sum));

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

$("li").each(function(index) {
  console.log(index + ": " + $(this).text());
});

Отложенные обратные вызовы обычно используются для обработки событий от пользователя, клиента и таймеров. Примеры можно найти в addEventListener, Аякс и XMLHttpRequest. [6]

Помимо использования обратных вызовов в исходном коде JavaScript, функции C, принимающие функцию, поддерживаются через js-ctypes. [7]

Красный и РЕБОЛ

[ редактировать ]

Следующий код REBOL / Red демонстрирует использование обратного вызова.

  • Поскольку для оповещения требуется строка, форма создает строку из результата вычисления.
  • Ключевое слово! значения (т. е. :calc-product и :calc-sum) заставляют интерпретатор возвращать код функции, а не вычислять ее с помощью функции.
  • Тип данных! ссылки в блоке! [плавать! целое число!] ограничивает тип значений, передаваемых в качестве аргументов.
Red [Title: "Callback example"]

calculate: func [
    num1 [number!] 
    num2 [number!] 
    callback-function [function!]
][
    callback-function num1 num2
]

calc-product: func [
    num1 [number!] 
    num2 [number!]
][
    num1 * num2
]

calc-sum: func [
    num1 [number!] 
    num2 [number!]
][
    num1 + num2
]

; alerts 75, the product of 5 and 15
alert form calculate 5 15 :calc-product

; alerts 20, the sum of 5 and 15
alert form calculate 5 15 :calc-sum

Пример цветовой анимации с использованием движка Roblox , который принимает необязательный обратный вызов .done:

wait(1)
local DT = wait()

function tween_color(object, finish_color, fade_time)
  local step_r = finish_color.r - object.BackgroundColor3.r
  local step_g = finish_color.g - object.BackgroundColor3.g
  local step_b = finish_color.b - object.BackgroundColor3.b
  local total_steps = 1/(DT*(1/fade_time))
  local completed;
  coroutine.wrap(function()
    for i = 0, 1, DT*(1 / fade_time) do
      object.BackgroundColor3 = Color3.new (
        object.BackgroundColor3.r + (step_r/total_steps),
        object.BackgroundColor3.g + (step_g/total_steps),
        object.BackgroundColor3.b + (step_b/total_steps)
      )
      wait()
    end
    if completed then
      completed()
    end
  end)()
  return {
    done = function(callback)
      completed = callback
    end
  }
end

tween_color(some_object, Color3.new(1, 0, 0), 1).done(function()
  print "Color tweening finished!"
end)

В следующем коде Python функция calculate принимает параметр operate который используется как блокирующий обратный вызов. calculate вызывается с square который действует как функция обратного вызова.

def square(val):
    return val ** 2
def calculate(operate, val):
    return operate(val)
# outputs: 25
calculate(square, 5)

В следующем коде Джулии функция calculate принимает параметр operate который используется как блокирующий обратный вызов. calculate вызывается с square который действует как функция обратного вызова.

julia> square(val)  = val^2
square (generic function with 1 method)
julia> calculate(operate,val) = operate(val)
calculate (generic function with 1 method)
julia> calculate(square,5)
25

См. также

[ редактировать ]
  1. ^ «Кулинарная книга Perl — 11.4. Получение ссылок на функции» . 2 июля 1999 года . Проверено 3 марта 2008 г.
  2. ^ «Продвинутое программирование на Perl — 4.2 Использование ссылок на подпрограммы» . 2 июля 1999 года . Проверено 3 марта 2008 г.
  3. ^ «Справочник по языку PHP — анонимные функции» . Проверено 8 июня 2011 г.
  4. ^ «Что нового в JDK 8» . oracle.com .
  5. ^ Белзер, Джек; Хольцман, Альберт Дж; Кент, Аллен, ред. (1979). Энциклопедия компьютерных наук и технологий: Том 12 . Марсель Деккер, Inc. п. 164. ИСБН  0-8247-2262-0 . Проверено 28 января 2024 г.
  6. ^ «Создание обратных вызовов JavaScript в компонентах» . Архив. Веб-документы UDN (страница документации). сек. JavaScript функционирует как обратные вызовы. Архивировано из оригинала 16 декабря 2021 г. Проверено 16 декабря 2021 г.
  7. ^ Холли, Бобби; Шепард, Эрик (ред.). «Объявление и использование обратных вызовов» . Документы. Сеть разработчиков Mozilla (страница документации). Архивировано из оригинала 17 января 2019 г. Проверено 16 декабря 2021 г.
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 070e48dda1db311e94a1fb67d6d55428__1714645740
URL1:https://arc.ask3.ru/arc/aa/07/28/070e48dda1db311e94a1fb67d6d55428.html
Заголовок, (Title) документа по адресу, URL1:
Callback (computer programming) - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)