Por que precisamos de "funções de retorno de chamada"?

16

Estou lendo o livro programming in Lua. Disse que

Os fechamentos fornecem uma ferramenta valiosa em muitos contextos. Como vimos, eles são úteis como argumentos para funções de ordem superior, como ordenação. Os fechamentos são valiosos para funções que constroem outras funções também, como nosso exemplo newCounter; esse mecanismo permite que os programas Lua incorporem técnicas sofisticadas de programação do mundo funcional. Os fechamentos também são úteis para funções de retorno de chamada. Um exemplo típico aqui ocorre quando você cria botões em um kit de ferramentas da GUI convencional. Cada botão possui uma função de retorno de chamada a ser chamada quando o usuário pressiona o botão; você deseja que botões diferentes façam coisas ligeiramente diferentes quando pressionados. Por exemplo, uma calculadora digital precisa de dez botões semelhantes, um para cada dígito. Você pode criar cada um deles com uma função como esta:

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

Parece que se eu chamar o digitButton, ele retornará o action(isso criará um fechamento), para que eu possa acessar o digitpassado para digitButton.

Minha pergunta é:

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


O autor disse:

Neste exemplo, assumimos que Button é uma função do kit de ferramentas que cria novos botões; label é o rótulo do botão; e action é o fechamento do retorno de chamada a ser chamado quando o botão é pressionado. O retorno de chamada pode ser chamado muito tempo depois que o digitButton executou sua tarefa e depois que o dígito da variável local ficou fora do escopo, mas ainda pode acessar essa variável.

de acordo com o autor, acho que um exemplo semelhante é assim:

function Button(t)
  -- maybe you should set the button here
  return t.action -- so that you can call this later
end

function add_to_display(digit)
  print ("Display the button label: " .. tostring(digit))
end

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

click_action = digitButton(10)
click_action()

portanto, the callback can be called a long time after digitButton did its task and after the local variable digit went out of scope.

Lucas Li
fonte

Respostas:

45

Cara 1 a Cara 2: ei cara, eu quero fazer alguma coisa quando um usuário clicar lá, me ligar de volta quando isso acontecer bem?

O Guy 2 chama de volta o Guy 1 quando um usuário clica aqui.

Florian Margaine
fonte
1
I como este call-back piada muito :-P
Lucas Li
6
Sou apenas eu? Não entendo como isso explica por que precisamos de funções de retorno de chamada.
Phant0m
2
É mais como o Guy 1 diz ao Guy 2 "quando for a hora certa, faça isso". O cara 2 segue seu dia e quando chega a hora certa faz isso. Se você quisesse que o Guy 1 fosse o chamador do Guy 2, isso está incorreto, pois o Guy 2 não chama de volta o Guy 1, ele chama a coisa a fazer. De fato, se o Guy 1 for o chamador do Guy 2, você terá um ciclo difícil em suas mãos nesse cenário. Se você quisesse que o Guy 1 fosse a chamada de volta, isso está incorreto, pois o callback não tem idéia de quem é o Guy 2 e certamente não pede que ele ligue.
RISM
Esta é uma explicação horrível.
29/09/17
21

Não, nunca retornará a ação. O botão irá executá- lo quando o usuário clicar nele. E essa é a resposta. Você precisa de funções de retorno de chamada quando desejar definir ações que devem ocorrer em outro componente em reação a um evento que você não controla (o loop de eventos, que faz parte do sistema, executará um método do botão que, por sua vez, executará a acção).

Jan Hudec
fonte
Eu não concordo com você. Eu editei minha postagem e adicionei alguns códigos. Eu ainda acho que a ação não é executada instantaneamente.
Lucas Li
2
Tim, você está certo - mas Jan. "O botão irá executá-lo quando o usuário clicar nele", não instantaneamente.
perfil
@AlexanderBrevig sim, eu ainda acho que a resposta de Jan é muito útil para mim. Isso me permite saber o que é chamada de volta e por que precisamos dela.
Lucas Li
O retorno de chamada é apenas um nome sujo que alguém adicionou a uma segunda função que funciona quando a primeira é acionada e talvez com base em um critério. Chame sua função well_done () e não teria diferença. O retorno de chamada é como Verificar, verificar é uma sequência de mensagens, geralmente duas, e o retorno de chamada é uma secuência de funções, geralmente duas. Se você encadeou mais de três retornos de chamada, parabéns, você gosta de fazer as coisas da maneira mais difícil.
M3nda
16

Eu acho que um exemplo melhor para o uso de retornos de chamada está em funções assíncronas. Não posso falar por Lua, mas em JavaScript, uma das ações assíncronas mais comuns é entrar em contato com um servidor via AJAX.

A chamada AJAX é assíncrona, ou seja, se você fizer algo assim:

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

o conteúdo do ifbloco não será executado corretamente de maneira confiável e, quando o fizer, é apenas porque a ajaxCall()função terminou antes da execução chegar à ifinstrução.

No entanto, os retornos de chamada eliminam esse problema, garantindo que a chamada assíncrona seja concluída antes de chamar a função necessária. Portanto, seu código mudaria para algo assim:

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

ajaxCall(ajaxCallback);

O objetivo disso é permitir que você faça coisas como coletar dados, sem interferir em outras coisas, como desenhar a interface. Se a coleta de dados fosse síncrona, a interface deixaria de responder à medida que o aplicativo aguardasse para obter os dados. Uma interface que não responde é muito ruim para a experiência do usuário, porque a maioria dos usuários pensa que o aplicativo "travou" e tenta finalizar o processo.

Para ver isso em ação, basta olhar para qualquer site que faça qualquer tipo de atualização sem recarregar a página inteira. Twitter, LinkedIn e os sites StackExchange são bons exemplos. A "rolagem interminável" do Twitter de feeds e as notificações do SE (para notificações do usuário e notificações de que uma pergunta tem nova atividade) são alimentadas por chamadas assíncronas. Quando você vê um botão giratório em algum lugar, isso significa que a seção fez uma chamada assíncrona e aguarda o término da chamada, mas você pode interagir com outras coisas na interface (e até fazer outras chamadas assíncronas). Quando a chamada terminar, ela reagirá e atualizará a interface de acordo.

Shauna
fonte
12

Você fez a pergunta

Por que precisamos de "funções de retorno de chamada"?

Tentarei respondê-lo de maneira concisa e clara (se eu falhar nisso, consulte o link do wiki na parte inferior desta resposta).

Os retornos de chamada são usados ​​para adiar a implementação específica de algo até o último momento possível.

O exemplo do botão é uma boa ilustração disso. Digamos que você queira ter um botão em seu aplicativo que imprima uma página na impressora; poderíamos imaginar um mundo em que você teria que codificar uma nova PrinterButtonclasse inteira para fazer isso.

Felizmente, temos a noção de retornos de chamada (herança e o uso do padrão de modelo também é um tipo de semântica de retorno de chamada), portanto, não precisamos fazer isso.

Em vez de reimplementar todos os aspectos de um botão, o que fazemos é adicionar à implementação do botão. O Buttontem o conceito de fazer algo quando pressionado. Simplesmente dizemos o que é isso.

Esse mecanismo de injeção de comportamento nas estruturas é chamado de retorno de chamada.

Exemplo:

Vamos injetar algum comportamento em um botão HTML:

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

Nesse caso, onclickaponta para um retorno de chamada, que para este exemplo é o print()método


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

AlexanderBrevig
fonte
Obrigado, depois de ler sua ilustração, vou ler o wiki cujos exemplos são apenas retornos de chamada síncronos.
Lucas Li
diga a uma função a resposta toda vez que houver um problema e você precise escutar os problemas; ensine uma função para resolver um problema que cuida de si mesma.
Joe
8

Por que precisamos de funções de retorno de chamada? Em que situações posso aplicar isso?

As funções de retorno de chamada são muito úteis na programação orientada a eventos. Eles permitem que você configure seu programa de forma que os eventos acionem o código correto. Isso é muito comum em programas com GUIs em que os usuários podem clicar em qualquer elemento da interface do usuário (como botões ou itens de menu) e o código apropriado será executado.

FrustratedWithFormsDesigner
fonte
1
+ Isso é o que as chamadas de retorno são boas para, mas eu odeio ter que escrevê-los :-)
Mike Dunlavey
Quando eu era apenas um calouro, meu professor nos ensinou como programar em MFC, mas ele nunca nos disse o que é callback :-(
Lucas Li
2

O que acontece sem retornos de chamada:

Cara 1: Ok, estou esperando que isso aconteça. (assobios, polegares giratórios)

Cara 2: Caramba! Por que o Guy 1 não está me mostrando coisas? Eu quero ver coisas acontecendo !! Onde estão as minhas coisas? Como alguém pode fazer alguma coisa por aqui?

Jim
fonte
1

Primeiro, o termo retorno de chamada refere-se a um fechamento que está sendo usado para algo.

Por exemplo, suponha que você crie uma função de fechamento e apenas armazene-a em uma variável. Não é um retorno de chamada, porque não está sendo usado para nada.

Mas, suponha que você crie um fechamento e armazene-o em algum lugar que será chamado quando algo acontecer. Agora é chamado de retorno de chamada.

Geralmente, os retornos de chamada são criados por diferentes partes do programa que as partes que os chamam. Portanto, é fácil imaginar que algumas partes do programa estão "retornando" as outras partes.

Simplificando, os retornos de chamada permitem que uma parte do programa diga à outra parte para fazer algo (qualquer coisa) quando algo acontecer.

Quanto às variáveis ​​mantidas vivas devido ao uso em um fechamento, esse é um recurso completamente diferente chamado upvalues ​​(também conhecido como "estendendo a vida útil das variáveis ​​locais" entre aqueles que não falam Lua). E é bastante útil também.

Jonathan Graef
fonte
Sim, Extending the lifetime of local variablesé também para dizer o termo upvalue.
Lucas Li
Hmm interessante. O termo valor alto parece ser específico para Lua, embora uma rápida pesquisa no Google mostre que ele foi usado ocasionalmente para descrever outros idiomas. Vou adicioná-lo à minha resposta.
Jonathan Graef
1

Os retornos de chamada existem desde os primeiros dias de Fortran, pelo menos. Por exemplo, se você tiver um solucionador de ODE (equação diferencial ordinária), como um solucionador de Runge-Kutta, ele poderá se parecer com o seguinte:

subroutine rungekutta(neq, t, tend, y, deriv)
  integer neq             ! number of differential equations
  double precision t      ! initial time
  double precision tend   ! final time
  double precision y(neq) ! state vector
  ! deriv is the callback function to evaluate the state derivative
  ...
  deriv(neq, t, y, yprime) ! call deriv to get the state derivative
  ...
  deriv(neq, t, y, yprime) ! call it several times
  ...
end subroutine

Ele permite que o chamador personalize o comportamento da sub-rotina, passando para ela uma função de finalidade especial a ser chamada quando necessário. Isso permite que o solucionador ODE seja usado para simular uma ampla variedade de sistemas.

Mike Dunlavey
fonte
1

Tropecei sobre isso e queria fornecer uma resposta mais atualizada ...

As funções de retorno de chamada nos permitem fazer algo com os dados posteriormente , permitindo que o restante do nosso código seja executado, em vez de esperar por eles. O código assíncrono nos permite esse luxo de executar qualquer coisa posteriormente . O exemplo mais legível das funções de retorno de chamada que encontrei são Promessas em JavaScript. No exemplo abaixo, toda vez que você vê a função (resultado) ou (newResult) ou (finalResult) ... essas são funções de retorno de chamada. O código entre colchetes é executado assim que os dados retornam do servidor. Somente nesse ponto faria sentido executar essas funções, pois agora os dados de que eles precisam estão disponíveis.

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

o código é retirado de ... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

Felizmente, isso ajuda alguém. Isto é o que me ajudou a entender retornos de chamada :)

NRV
fonte
1
este não parece oferecer nada substancial sobre pontos feitos e explicado em anteriores 9 respostas
mosquito
1
Talvez para você, mas foi isso que realmente tornou os retornos claros para mim, então pensei em compartilhar. Na IMO, a legibilidade do exemplo é o que faz valer a pena. Tenho certeza de que podemos concordar que a legibilidade do código é um longo caminho, especialmente para iniciantes.
NRV 27/03
-2

Eu não sei lua, mas em geral métodos de retorno de chamada alguns que gostam de multithreading. Por exemplo, na programação de desenvolvimento de aplicativos móveis, a maioria dos aplicativos funciona como o envio de solicitação ao servidor e a reprodução da interface do usuário com os dados, como resposta do servidor. Quando o usuário envia uma solicitação ao servidor, leva um tempo para obter a resposta do servidor, mas a melhor interface do usuário do UX não deve ser bloqueada.

Por isso, usamos vários threads para realizar operações paralelas. Quando obtemos a resposta do servidor, precisamos atualizar a interface do usuário. temos que notificar a partir desse segmento para atualizar. do mundo funcional. Esse tipo de chamada de função é chamado de método de retorno de chamada. Quando você chama esses métodos, o controle deve retornar ao thread principal. Por exemplo, métodos de retorno de chamada são blocos no objetivo-C.

AMohan
fonte
Sim, o que você disse é consistente com o meu entendimento depois de ler o livro.
Lucas Li
2
Os métodos de retorno de chamada não são nada como multithreading. Os métodos de retorno de chamada são simplesmente ponteiros de função (delegados). Os tópicos são sobre a alternância / utilização de contexto da CPU. Um contraste altamente simplificado seria que o threading trata da otimização da CPU para UX, enquanto os retornos de chamada referem-se à troca de memória por polimorfismo. Simplesmente porque os threads podem executar retornos de chamada não significa que os threading e os retornos de chamada sejam semelhantes. Os threads também executam métodos estáticos em classes estáticas, mas os tipos threading e static não são semelhantes.
RISM