Como explicar retornos de chamada em inglês simples? Como eles são diferentes de chamar uma função de outra função, tendo algum contexto da função de chamada? Como o poder deles pode ser explicado a um programador iniciante?
Como explicar retornos de chamada em inglês simples? Como eles são diferentes de chamar uma função de outra função, tendo algum contexto da função de chamada? Como o poder deles pode ser explicado a um programador iniciante?
Respostas:
Freqüentemente, um aplicativo precisa executar diferentes funções com base em seu contexto / estado. Para isso, usamos uma variável na qual armazenamos as informações sobre a função a ser chamada. De acordo com sua necessidade, o aplicativo definirá essa variável com as informações sobre a função a ser chamada e chamará a função usando a mesma variável.
Em javascript, o exemplo está abaixo. Aqui usamos o argumento do método como uma variável em que armazenamos informações sobre a função.
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]
function(arg)
) na processArray(arr,callback)
função
Vou tentar manter esse morto simples. Um "retorno de chamada" é qualquer função chamada por outra função que aceita a primeira função como parâmetro. Na maioria das vezes, um "retorno de chamada" é uma função que é chamada quando algo acontece. Que algo pode ser chamado de "evento" na fala do programador.
Imagine este cenário: você está esperando um pacote em alguns dias. O pacote é um presente para o seu vizinho. Portanto, uma vez que você obtém o pacote, deseja que ele seja trazido para os vizinhos. Você está fora da cidade e deixa instruções para seu cônjuge.
Você poderia dizer a eles para pegar o pacote e levá-lo aos vizinhos. Se o seu cônjuge fosse tão estúpido quanto um computador, eles se sentariam à porta e esperariam o pacote até ele chegar (NÃO FAZENDO ALGO MAIS) e, depois que chegassem, o levariam aos vizinhos. Mas há uma maneira melhor. Diga ao seu cônjuge que, uma vez que receberem o pacote, eles deverão trazê-lo para os vizinhos. Então, eles podem seguir a vida normalmente até receberem o pacote.
Em nosso exemplo, o recebimento do pacote é o "evento" e o envio para os vizinhos é o "retorno de chamada". Seu cônjuge "executa" suas instruções para trazer o pacote somente quando o pacote chegar. Muito melhor!
Esse tipo de pensamento é óbvio na vida cotidiana, mas os computadores não têm o mesmo tipo de bom senso. Considere como os programadores normalmente gravam em um arquivo:
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
Aqui, esperamos que o arquivo seja aberto antes de escrever nele. Isso "bloqueia" o fluxo de execução e nosso programa não pode fazer nenhuma das outras coisas que possa precisar! E se pudéssemos fazer isso:
# 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!
Acontece que fazemos isso com algumas linguagens e estruturas. É muito legal! Confira o Node.js para obter algumas práticas reais com esse tipo de pensamento.
open
funciona. É plausível que open
possa bloquear internamente enquanto aguarda o sistema operacional fazer sua magia negra, na qual o retorno de chamada é executado. Não há diferença no resultado nesse caso.
Como explicar retornos de chamada em inglês simples?
Em inglês simples, uma função de retorno de chamada é como um Trabalhador que "liga de volta" para seu Gerente quando ele conclui uma Tarefa .
Como eles são diferentes de chamar uma função de outra função, tendo algum contexto da função de chamada?
É verdade que você está chamando uma função de outra função, mas a chave é que o retorno de chamada é tratado como um Objeto, para que você possa alterar qual Função chamar com base no estado do sistema (como o Padrão de Design da Estratégia).
Como o poder deles pode ser explicado a um programador iniciante?
O poder dos retornos de chamada pode ser facilmente visto em sites no estilo AJAX, que precisam extrair dados de um servidor. O download dos novos dados pode levar algum tempo. Sem retornos de chamada, toda a interface do usuário "congelaria" durante o download dos novos dados ou você precisaria atualizar a página inteira em vez de apenas parte dela. Com um retorno de chamada, você pode inserir uma imagem "now loading" e substituí-la pelos novos dados depois de carregados.
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)');
}
Aqui está um exemplo com um retorno de chamada, usando getJSON do jQuery :
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
}
Freqüentemente, o retorno de chamada precisa acessar state
da função de chamada usando a closure
, que é como o Trabalhador que precisa obter informações do Gerente antes que ele possa concluir sua Tarefa . Para criar o closure
, você pode incorporar a função para que ela veja os dados no contexto de chamada:
/* 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);
Finalmente, aqui está uma definição de closure
de Douglas Crockford :
As funções podem ser definidas dentro de outras funções. A função interna tem acesso aos vars e aos parâmetros da função externa. Se uma referência a uma função interna sobreviver (por exemplo, como uma função de retorno de chamada), os vars da função externa também sobreviverão.
Veja também:
Fico surpreso ao ver tantas pessoas inteligentes falhando em enfatizar a realidade que a palavra "retorno de chamada" passou a ser usada de duas maneiras inconsistentes.
Ambas as formas envolvem a personalização de uma função passando funcionalidade adicional (uma definição de função, anônima ou nomeada) para uma função existente. ie
customizableFunc(customFunctionality)
Se a funcionalidade personalizada estiver simplesmente conectada ao bloco de código, você personalizou a função, assim.
customizableFucn(customFunctionality) {
var data = doSomthing();
customFunctionality(data);
...
}
Embora esse tipo de funcionalidade injetada seja freqüentemente chamado de "retorno de chamada", não há nada contingente. Um exemplo muito óbvio é o método forEach, no qual uma função personalizada é fornecida como um argumento a ser aplicado a cada elemento em uma matriz para modificar a matriz.
Mas isso é fundamentalmente distinto do uso de funções de "retorno de chamada" para programação assíncrona , como no AJAX ou node.js ou simplesmente na atribuição de funcionalidade a eventos de interação do usuário (como cliques do mouse). Nesse caso, a idéia é aguardar a ocorrência de um evento contingente antes de executar a funcionalidade customizada. Isso é óbvio no caso da interação do usuário, mas também é importante nos processos de E / S (entrada / saída) que podem levar tempo, como a leitura de arquivos do disco. É aqui que o termo "retorno de chamada" faz o sentido mais óbvio. Depois que um processo de E / S é iniciado (como solicitar que um arquivo seja lido do disco ou de um servidor para retornar dados de uma solicitação http), é assíncronaprograma não espera para terminar. Ele pode prosseguir com as tarefas agendadas a seguir e responder apenas com a funcionalidade personalizada após ter sido notificado de que o arquivo de leitura ou a solicitação http foi concluída (ou que falhou) e que os dados estão disponíveis para a funcionalidade personalizada. É como ligar para uma empresa por telefone e deixar seu número de "retorno de chamada", para que eles possam ligar quando alguém estiver disponível para entrar em contato com você. É melhor do que ficar na fila para quem sabe quanto tempo e não poder atender a outros assuntos.
O uso assíncrono envolve inerentemente alguns meios de escutar o evento desejado (por exemplo, a conclusão do processo de E / S) para que, quando ele ocorra (e somente quando ele ocorra), a funcionalidade "retorno de chamada" personalizada seja executada. No exemplo óbvio do AJAX, quando os dados realmente chegam do servidor, a função "retorno de chamada" é acionada para usar esses dados para modificar o DOM e, portanto, redesenhar a janela do navegador nessa extensão.
Para recapitular. Algumas pessoas usam a palavra "retorno de chamada" para se referir a qualquer tipo de funcionalidade personalizada que pode ser injetada em uma função existente como argumento. Mas, pelo menos para mim, o uso mais apropriado da palavra é onde a função "retorno de chamada" injetada é usada de forma assíncrona - para ser executada somente na ocorrência de um evento que está aguardando para ser notificado.
Array.prototype.forEach()
e a função passada como um argumento para setTimeout()
, e eles são cavalos de cores diferentes, na medida em que você pensa sobre seu programa .
Em termos não programadores, um retorno de chamada é um preenchimento em branco em um programa.
Um item comum em muitos formulários em papel é "Pessoa para ligar em caso de emergência". Há uma linha em branco lá. Você escreve o nome e o número de telefone de alguém. Se ocorrer uma emergência, essa pessoa será chamada.
Isso é fundamental. Você não altera o formulário (o código, geralmente o de outra pessoa). No entanto, você pode preencher as informações que faltam ( seu número).
Exemplo 1:
Os retornos de chamada são usados como métodos personalizados, possivelmente para adicionar / alterar o comportamento de um programa. Por exemplo, pegue um código C que executa uma função, mas não sabe como imprimir a saída. Tudo o que pode fazer é criar uma string. Quando tenta descobrir o que fazer com a string, vê uma linha em branco. Mas, o programador deu a você o espaço em branco para escrever sua chamada de retorno!
Neste exemplo, você não use um lápis para preencher um espaço em branco em uma folha de papel, você usa a função set_print_callback(the_callback)
.
set_print_callback
é o lápisthe_callback
é sua informação que você está preenchendo.Agora você preencheu esta linha em branco no programa. Sempre que precisar imprimir a saída, ela olhará para essa linha em branco e seguirá as instruções (ex: chamar a função que você coloca lá). Praticamente, isso permite a possibilidade de imprimir na tela, em um arquivo de log, em uma impressora, através de uma conexão de rede ou qualquer combinação deles. Você preencheu o espaço em branco com o que deseja fazer.
Exemplo 2:
Quando lhe dizem que precisa ligar para um número de emergência, você lê o que está escrito no formulário em papel e liga para o número que lê. Se essa linha estiver em branco, nada será feito.
A programação da GUI funciona da mesma maneira. Quando um botão é clicado, o programa precisa descobrir o que fazer a seguir. Ele vai e procura o retorno de chamada. Esse retorno de chamada acontece em um espaço em branco rotulado "Aqui está o que você faz quando o Botão1 é clicado"
A maioria dos IDEs preencherá automaticamente o espaço em branco para você (escreva o método básico) quando você solicitar (por exemplo button1_clicked
). No entanto, esse espaço em branco pode ter qualquer método que você queira, por favor . Você pode chamar o método run_computations
ou butter_the_biscuits
desde que coloque o nome do retorno de chamada no espaço em branco apropriado. Você pode colocar "555-555-1212" no número de emergência em branco. Não faz muito sentido, mas é permitido.
Nota final: a linha em branco que você está preenchendo com o retorno de chamada? Pode ser apagado e reescrito à vontade. (se você deve ou não é outra questão, mas isso faz parte do poder deles)
Sempre melhor começar com um exemplo :).
Vamos supor que você tenha dois módulos A e B.
Você deseja que o módulo A seja notificado quando ocorrer algum evento / condição no módulo B. No entanto, o módulo B não tem idéia do seu módulo A. Tudo o que sabe é um endereço para uma função específica (do módulo A) por meio de um ponteiro de função. fornecido pelo módulo A.
Portanto, tudo o que B precisa fazer agora é "retorno de chamada" no módulo A quando um evento / condição específico ocorre usando o ponteiro de função. A pode fazer um processamento adicional dentro da função de retorno de chamada.
*) Uma vantagem clara aqui é que você está abstraindo tudo sobre o módulo A do módulo B. O módulo B não precisa se preocupar com quem / o módulo A.
Johny, o programador, precisa de um grampeador, então ele vai até o departamento de suprimentos de escritório e pede um. Depois de preencher o formulário de solicitação, ele pode ficar parado ali e esperar o atendente ir procurar o grampeador no armazém (como uma chamada de função de bloqueio ) ou vá fazer outra coisa enquanto isso.
como isso geralmente leva tempo, johny coloca uma nota junto com o formulário de solicitação, solicitando que liguem para ele quando o grampeador estiver pronto para a coleta, portanto, enquanto isso, ele pode fazer outra coisa como cochilar em sua mesa.
Imagine que você precisa de uma função que retorne 10 ao quadrado para escrever uma função:
function tenSquared() {return 10*10;}
Mais tarde, você precisa de 9 ao quadrado para escrever outra função:
function nineSquared() {return 9*9;}
Eventualmente, você substituirá tudo isso por uma função genérica:
function square(x) {return x*x;}
O mesmo pensamento se aplica aos retornos de chamada. Você tem uma função que faz alguma coisa e, quando concluída, chama doA:
function computeA(){
...
doA(result);
}
Mais tarde, você deseja a mesma função exata para chamar doB, em vez disso, poderá duplicar toda a função:
function computeB(){
...
doB(result);
}
Ou você pode passar uma função de retorno de chamada como uma variável e só precisa ter a função uma vez:
function compute(callback){
...
callback(result);
}
Então você só precisa chamar computação (doA) e computação (doB).
Além de simplificar o código, ele permite que o código assíncrono saiba que ele foi concluído ao chamar sua função arbitrária na conclusão, semelhante a quando você liga para alguém no telefone e deixa um número de retorno de chamada.
Você se sente mal e vai ao médico. Ele examina você e determina que você precisa de algum medicamento. Ele prescreve alguns remédios e chama a receita na sua farmácia local. Você vai para casa. Mais tarde, sua farmácia liga para informar que sua prescrição está pronta. Você vai buscá-lo.
Há dois pontos a serem explicados: um é como um retorno de chamada funciona (passando uma função que pode ser chamada sem nenhum conhecimento de seu contexto), o outro para o qual é usado (manipulando eventos de forma assíncrona).
A analogia de esperar a chegada de um pacote que tenha sido usado por outras respostas é boa para explicar as duas coisas. Em um programa de computador, você diria ao computador para esperar uma parcela. Normalmente, ele ficava ali sentado e aguardava (e não fazia mais nada) até que o pacote chegasse, possivelmente indefinidamente, se nunca chegar. Para os humanos, isso parece bobagem, mas sem outras medidas, isso é totalmente natural para um computador.
Agora a chamada seria a campainha na sua porta da frente. Você fornece ao serviço de encomendas uma maneira de notificá-lo da chegada da encomenda sem que eles precisem saber onde (mesmo se) você está na casa ou como a campainha funciona. (Por exemplo, alguns "sinos" realmente enviam uma ligação). Como você forneceu uma "função de retorno de chamada" que pode ser "chamada" a qualquer momento, fora do contexto, agora você pode parar de sentar na varanda da frente e "lidar com o evento "(de chegada de encomendas) sempre que for a hora.
Imagine que uma amiga está saindo de sua casa e você diz a ela "Me ligue quando chegar em casa para que eu saiba que você chegou em segurança"; isso é (literalmente) uma ligação de volta . É isso que é uma função de retorno de chamada, independentemente do idioma. Você deseja que algum procedimento repasse o controle para você quando concluir alguma tarefa, portanto, você atribui a ele uma função para chamá-lo.
Em Python, por exemplo,
grabDBValue( (lambda x: passValueToGUIWindow(x) ))
grabDBValue
pode ser gravado para pegar apenas um valor de um banco de dados e permitir que você especifique o que realmente fazer com o valor, para que ele aceite uma função. Você não sabe quando ou se grabDBValue
retornará, mas se / quando ocorrer, você sabe o que deseja que faça. Aqui, passo uma função anônima (ou lambda ) que envia o valor para uma janela da GUI. Eu poderia facilmente mudar o comportamento do programa fazendo o seguinte:
grabDBValue( (lambda x: passToLogger(x) ))
Os retornos de chamada funcionam bem em idiomas onde as funções são valores de primeira classe , assim como os números inteiros usuais, cadeias de caracteres, booleanos, etc. em Java, o chamador solicitará uma classe estática de um determinado tipo com um determinado nome de método, pois não há funções ("métodos", realmente) fora das classes; e na maioria das outras linguagens dinâmicas, você pode simplesmente passar uma função com sintaxe simples.
Em idiomas com escopo lexical (como Scheme ou Perl), você pode fazer um truque como este:
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
nesse caso, será 6
porque o retorno de chamada tem acesso às variáveis declaradas no ambiente lexical em que foi definido. O escopo lexical e as chamadas de retorno anônimas são uma combinação poderosa que requer mais estudos para o programador iniciante.
Você tem algum código que deseja executar. Normalmente, quando você o chama, espera que ele termine antes de continuar (o que pode fazer com que seu aplicativo fique cinza / produza um tempo de rotação para um cursor).
Um método alternativo é executar esse código em paralelo e continuar com seu próprio trabalho. Mas e se o seu código original precisar fazer coisas diferentes, dependendo da resposta do código que chamou? Bem, nesse caso, você pode passar o nome / local do código que deseja chamar quando terminar. Este é um "retorno de chamada".
Código normal: Peça Informações-> Informações do Processo-> Lide com os resultados do Processamento-> Continue fazendo outras coisas.
Com retornos de chamada: Solicite informações-> Informações do processo-> Continue fazendo outras coisas. E em algum momento posterior-> Lide com os resultados do processamento.
Sem retorno de chamada nem outros recursos especiais de programação (como threading e outros), um programa é exatamente uma sequência de instruções executadas seqüencialmente uma após a outra , e mesmo com um tipo de "comportamento dinâmico" determinado por determinadas condições, todos os cenários possíveis deve ser previamente programado .
Portanto, se precisarmos fornecer um comportamento dinâmico real a um programa, podemos usar o retorno de chamada. Com o retorno de chamada, você pode instruir por parâmetros, um programa para chamar outro programa, fornecendo alguns parâmetros definidos anteriormente e esperar alguns resultados ( esta é a assinatura do contrato ou da operação ), para que esses resultados possam ser produzidos / processados por um programa de terceiros que não foi não era conhecido anteriormente.
Essa técnica é a base do polimorfismo aplicado a programas, funções, objetos e todas as outras unidades de código executadas por computadores.
O mundo humano usado como exemplo para retorno de chamada é bem explicado quando você está fazendo algum trabalho, vamos supor que você seja um pintor ( aqui você é o programa principal, que pinta ) e, às vezes, ligue para o seu cliente para pedir que ele aprove o resultado do seu trabalho , ele decide se a imagem é boa ( seu cliente é o programa de terceiros ).
No exemplo acima, você é um pintor e "delega" a outros o trabalho para aprovar o resultado, a imagem é o parâmetro e cada novo cliente (a "função" chamada de retorno) altera o resultado do seu trabalho, decidindo o que ele deseja sobre a imagem ( a decisão tomada pelos clientes é o resultado retornado da "função de retorno de chamada" ).
Espero que esta explicação possa ser útil.
Vamos fingir que você deveria me dar uma tarefa potencialmente demorada: obter os nomes das cinco primeiras pessoas únicas que você se deparar. Isso pode levar dias se eu estiver em uma área escassamente povoada. Você não está realmente interessado em ficar sentado em suas mãos enquanto eu corro por aí, então você diz: "Quando tiver a lista, ligue para meu celular e leia de novo para mim. Aqui está o número".
Você me forneceu uma referência de retorno de chamada - uma função que devo executar para entregar mais processamento.
Em JavaScript, pode ser algo como isto:
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);
Provavelmente isso poderia ser melhorado de várias maneiras. Por exemplo, você pode fornecer um segundo retorno de chamada: se demorar mais de uma hora, ligue para o telefone vermelho e diga à pessoa que responde que você atingiu o tempo limite.
Os retornos de chamada são descritos com mais facilidade em termos de sistema telefônico. Uma chamada de função é análoga a ligar para alguém no telefone, fazer uma pergunta, obter uma resposta e desligar; adicionar um retorno de chamada altera a analogia para que, depois de fazer uma pergunta, você também forneça seu nome e número para que ela possa ligar de volta com a resposta. - Paul Jakubik, "Implementações de retorno de chamada em C ++"
Um retorno de chamada é uma função que será chamada por uma segunda função. Esta segunda função não sabe de antemão qual função chamará. Portanto, a identidade da função de retorno de chamada é armazenada em algum lugar ou transmitida para a segunda função como parâmetro. Essa "identidade", dependendo da linguagem de programação, pode ser o endereço do retorno de chamada ou algum outro tipo de ponteiro, ou pode ser o nome da função. O principal é o mesmo, armazenamos ou passamos algumas informações que identificam inequivocamente a função.
Quando chegar a hora, a segunda função pode chamar o retorno de chamada, fornecendo parâmetros dependendo das circunstâncias naquele momento. Pode até escolher o retorno de chamada entre um conjunto de possíveis retornos de chamada. A linguagem de programação deve fornecer algum tipo de sintaxe para permitir que a segunda função chame o retorno de chamada, conhecendo sua "identidade".
Esse mecanismo tem muitos usos possíveis. Com os retornos de chamada, o designer de uma função pode permitir que ela seja personalizada fazendo com que ela chame os retornos de chamada fornecidos. Por exemplo, uma função de classificação pode usar um retorno de chamada como parâmetro, e esse retorno de chamada pode ser uma função para comparar dois elementos para decidir qual deles vem primeiro.
A propósito, dependendo da linguagem de programação, a palavra "função" na discussão acima pode ser substituída por "bloco", "encerramento", "lambda" etc.
Normalmente enviamos variáveis para funções. Suponha que você tenha uma tarefa em que a variável precisa ser processada antes de ser fornecida como argumento - você pode usar o retorno de chamada.
function1(var1, var2)
é o caminho usual.
E se eu quiser var2
ser processado e depois enviado como argumento?
function1(var1, function2(var2))
Este é um tipo de retorno de chamada - onde function2
executa algum código e retorna uma variável de volta à função inicial.
Uma explicação metafórica:
Tenho uma encomenda que quero entregar a um amigo e também quero saber quando meu amigo a recebe.
Então, levo o pacote para os correios e peço que entreguem. Se eu quiser saber quando meu amigo recebe o pacote, tenho duas opções:
(a) Posso esperar nos correios até que seja entregue.
(b) Receberei um email quando ele for entregue.
A opção (b) é análoga a um retorno de chamada.
Para ensinar retornos de chamada, você deve ensinar o ponteiro primeiro. Depois que os alunos entenderem a idéia de apontar para uma variável, a idéia de retornos de chamada ficará mais fácil. Supondo que você esteja usando C / C ++, essas etapas podem ser seguidas.
Pode haver muito mais coisas. Envolva os alunos e eles descobrirão. Espero que isto ajude.
Em inglês simples, um retorno de chamada é uma promessa. Joe, Jane, David e Samantha compartilham uma carona para trabalhar. Joe está dirigindo hoje. Jane, David e Samantha têm algumas opções:
Opção 1: É mais como um exemplo de pesquisa em que Jane ficaria presa em um "loop", verificando se Joe estava do lado de fora. Jane não pode fazer mais nada nesse meio tempo.
Opção 2: este é o exemplo de retorno de chamada. Jane diz a Joe para tocar a campainha quando ele está do lado de fora. Ela lhe dá uma "função" para tocar a campainha da porta. Joe não precisa saber como a campainha da porta funciona ou onde ela está, ele só precisa chamar essa função, ou seja, tocar a campainha da porta quando estiver lá.
Os retornos de chamada são direcionados por "eventos". Neste exemplo, o "evento" é a chegada de Joe. No Ajax, por exemplo, os eventos podem ser "sucesso" ou "falha" da solicitação assíncrona e cada um pode ter o mesmo ou diferentes retornos de chamada.
Em termos de aplicativos JavaScript e retornos de chamada. Também precisamos entender "fechamentos" e o contexto do aplicativo. O que "isso" se refere pode facilmente confundir os desenvolvedores de JavaScript. Neste exemplo, dentro do método / retorno de chamada "ring_the_door_bell ()" de cada pessoa, pode haver outros métodos que cada pessoa precisa executar com base na rotina matinal ex. "desligue a televisão()". Queremos que "this" se refira ao objeto "Jane" ou ao objeto "David", para que cada um possa configurar o que mais precisar antes de Joe buscá-los. É aqui que a configuração do retorno de chamada com Joe exige a paródia do método, para que "this" se refira ao objeto correto.
Espero que ajude!
Eu acho que é uma tarefa bastante fácil de explicar.
No primeiro retorno de chamada, são apenas funções comuns.
E, além disso, chamamos essa função (vamos chamá-la de A) de dentro de outra função (vamos chamá-la de B).
A mágica disso é que eu decido qual função deve ser chamada pela função de fora de B.
No momento em que escrevo a função BI, não sei qual função de retorno de chamada deve ser chamada. No momento em que chamo a função BI, também digo a essa função para chamar a função A. Isso é tudo.
O que é uma função de retorno de chamada?
A resposta simples para essa primeira pergunta é que uma função de retorno de chamada é uma função chamada por meio de um ponteiro de função. Se você passar o ponteiro (endereço) de uma função como argumento para outro, quando esse ponteiro é usado para chamar a função, ele aponta para ele e diz que é feita uma chamada de retorno.
É difícil rastrear a função de retorno de chamada, mas às vezes é muito útil. Especialmente quando você está projetando bibliotecas. A função de retorno de chamada é como pedir ao usuário que lhe forneça um nome de função e você a chamará sob determinada condição.
Por exemplo, você escreve um cronômetro de retorno de chamada. Permite especificar a duração e qual função chamar, e a função será retornada de acordo. “Execute myfunction () a cada 10 segundos por 5 vezes”
Ou você pode criar um diretório de funções, passando uma lista do nome da função e solicitar à biblioteca que retorne a chamada adequadamente. "Sucesso de retorno de chamada () se for bem-sucedido, falha de retorno de chamada () se falhou."
Vamos ver um exemplo simples de ponteiro de função
void cbfunc()
{
printf("called");
}
int main ()
{
/* function pointer */
void (*callback)(void);
/* point to your callback function */
callback=(void *)cbfunc;
/* perform callback */
callback();
return 0;
}
Como passar o argumento para a função de retorno de chamada?
Observado que o ponteiro de função para implementar retorno de chamada recebe nulo *, o que indica que ele pode receber qualquer tipo de variável, incluindo estrutura. Portanto, você pode passar vários argumentos por estrutura.
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;
}
Um retorno de chamada é um método que está programado para ser executado quando uma condição for atendida.
Um exemplo do "mundo real" é uma loja de videogame local. Você está esperando o Half-Life 3. Em vez de ir à loja todos os dias para ver se está, você registra seu email em uma lista para ser notificado quando o jogo estiver disponível. O email se torna seu "retorno de chamada" e a condição a ser atendida é a disponibilidade do jogo.
Um exemplo de "programadores" é uma página da web em que você deseja executar uma ação quando um botão é clicado. Você registra um método de retorno de chamada para um botão e continua executando outras tarefas. Quando / se o usuário clicar no botão, o navegador examinará a lista de retornos de chamada para esse evento e chamará seu método.
Um retorno de chamada é uma maneira de manipular eventos de forma assíncrona. Você nunca pode saber quando o retorno de chamada será executado ou se será executado. A vantagem é que ele libera seu programa e ciclos de CPU para executar outras tarefas enquanto aguarda a resposta.
Puro e simples: um retorno de chamada é uma função que você atribui a outra função, para que possa chamá- lo.
Geralmente é chamado quando alguma operação é concluída. Como você cria o retorno de chamada antes de entregá-lo à outra função, é possível inicializá-lo com informações de contexto do site de chamada. É por isso que é chamada de chamada * back * - a primeira função chama de volta ao contexto de onde foi chamada.
“Na programação de computadores, um retorno de chamada é uma referência ao código executável, ou um pedaço de código executável, que é passado como argumento para outro código. Isso permite que uma camada de software de nível inferior chame uma sub-rotina (ou função) definida em uma camada de nível superior. ” - Wikipedia
Retorno de chamada em C usando o ponteiro de função
Em C, o retorno de chamada é implementado usando o Ponteiro de Função. Ponteiro de função - como o nome sugere, é um ponteiro para uma função.
Por exemplo, int (* ptrFunc) ();
Aqui, ptrFunc é um ponteiro para uma função que não aceita argumentos e retorna um número inteiro. NÃO esqueça de colocar parênteses, caso contrário, o compilador assumirá que ptrFunc é um nome de função normal, que não aceita nada e retorna um ponteiro para um número inteiro.
Aqui está um código para demonstrar o ponteiro da função.
#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;
}
Agora vamos tentar entender o conceito de retorno de chamada em C usando o ponteiro de função.
O programa completo possui três arquivos: callback.c, reg_callback.he 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)();
}
Se rodarmos este programa, a saída será
Este é um programa que demonstra a função de retorno de chamada dentro de register_callback dentro de my_callback de volta no programa principal
A função de camada superior chama uma função de camada inferior como uma chamada normal e o mecanismo de retorno de chamada permite que a função de camada inferior chame a função de camada superior através de um ponteiro para uma função de retorno de chamada.
Retorno de chamada em Java usando a interface
Java não possui o conceito de ponteiro de função. Implementa o mecanismo de retorno de chamada através do seu mecanismo de interface. Aqui, em vez de um ponteiro de função, declaramos uma interface com um método que será chamado quando o destinatário concluir sua tarefa.
Deixe-me demonstrá-lo através de um exemplo:
A interface de retorno de chamada
public interface Callback
{
public void notify(Result result);
}
O chamador ou a classe de nível superior
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
}
}
A função Callee ou a camada inferior
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
}
}
Retorno de Chamada Usando o Padrão EventListener
Esse padrão é usado para notificar de 0 a n números de Observadores / Ouvintes que uma tarefa específica foi concluída
A diferença entre o mecanismo de retorno de chamada e o mecanismo EventListener / Observer é que, no retorno de chamada, o destinatário notifica o chamador único, enquanto no Eventlisener / Observer, o destinatário pode notificar qualquer pessoa interessada nesse evento (a notificação pode ir para outras partes do aplicativo que não acionou a tarefa)
Deixe-me explicar através de um exemplo.
A interface do evento
public interface Events {
public void clickEvent();
public void longClickEvent();
}
Widget de classe
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);
}
}
Botão de classe
public class Button extends Widget{
private String mButtonText;
public Button (){
}
public String getButtonText() {
return mButtonText;
}
public void setButtonText(String buttonText) {
this.mButtonText = buttonText;
}
}
Caixa de seleção Classe
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;
}
}
Classe de atividade
pacote 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();
}
}
Outra classe
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...");
}
}
Classe principal
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);
}
}
Como você pode ver no código acima, temos uma interface chamada events que basicamente lista todos os eventos que podem acontecer para nosso aplicativo. A classe Widget é a classe base de todos os componentes da interface do usuário, como botão e caixa de seleção. Esses componentes da interface do usuário são os objetos que realmente recebem os eventos do código da estrutura. A classe Widget implementa a interface Events e também possui duas interfaces aninhadas, a saber, OnClickEventListener e OnLongClickEventListener
Essas duas interfaces são responsáveis por ouvir os eventos que podem ocorrer nos componentes da interface do usuário derivados do Widget, como Botão ou Caixa de seleção. Portanto, se compararmos este exemplo com o exemplo anterior de retorno de chamada usando a interface Java, essas duas interfaces funcionarão como a interface de retorno de chamada. Portanto, o código de nível superior (Atividade aqui) implementa essas duas interfaces. E sempre que um evento ocorrer em um widget, o código de nível superior (ou o método dessas interfaces implementadas no código de nível superior, que é aqui Atividade) será chamado.
Agora, deixe-me discutir a diferença básica entre o padrão de retorno de chamada e ouvinte de evento. Como mencionamos, usando o retorno de chamada, o Callee pode notificar apenas um único chamador. Porém, no caso do padrão EventListener, qualquer outra parte ou classe do Aplicativo pode se registrar para os eventos que podem ocorrer no Botão ou na Caixa de Seleção. O exemplo desse tipo de classe é o OtherClass. Se você vir o código da OtherClass, descobrirá que ele se registrou como ouvinte do ClickEvent que pode ocorrer no botão definido na Atividade. Parte interessante é que, além da Atividade (o chamador), essa OtherClass também será notificada sempre que o evento click ocorrer no botão.
Os retornos de chamada permitem que você insira seu próprio código em outro bloco de código a ser executado em outro momento, que modifica ou adiciona ao comportamento desse outro bloco de código para atender às suas necessidades. Você ganha flexibilidade e capacidade de personalização enquanto pode ter um código mais sustentável.
Menos código rígido = mais fácil de manter e alterar = menos tempo = mais valor comercial = grandiosidade.
Por exemplo, em javascript, usando Underscore.js, você pode encontrar todos os elementos pares em uma matriz como esta:
var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
=> [2, 4, 6]
Exemplo cortesia de Underscore.js: http://documentcloud.github.com/underscore/#filter
[editado] quando temos duas funções, digamos functionA e functionB , se functionA depende da funçãoB .
então chamamos functionB como uma função de retorno de chamada . isso é amplamente usado no framework Spring.
Pense em um método como dar uma tarefa a um colega de trabalho. Uma tarefa simples pode ser a seguinte:
Solve these equations:
x + 2 = y
2 * x = 3 * y
Seu colega de trabalho faz as contas diligentemente e fornece o seguinte resultado:
x = -6
y = -4
Mas seu colega de trabalho tem um problema, ele nem sempre entende as notações, como ^
, mas ele as entende pela descrição. Tais como exponent
. Toda vez que ele encontra um desses, você recebe de volta o seguinte:
I don't understand "^"
Isso exige que você reescreva todo o conjunto de instruções novamente depois de explicar o que o personagem significa para o seu colega de trabalho, e ele nem sempre se lembra entre as perguntas. E ele também tem dificuldade em se lembrar das suas dicas, como é o caso de me perguntar. Ele sempre segue suas instruções escritas da melhor maneira possível.
Você pensa em uma solução, basta adicionar o seguinte a todas as suas instruções:
If you have any questions about symbols, call me at extension 1234 and I will tell you its name.
Agora, sempre que ele tem um problema, ele liga e pergunta, em vez de dar uma resposta ruim e reiniciar o processo.
Isso em termos de download de uma página da web:
Seu programa é executado em um telefone celular e está solicitando a página da web http://www.google.com . Se você escrever seu programa de forma síncrona, a função que você escreve para baixar os dados estará em execução contínua até que todos os dados sejam baixados. Isso significa que sua interface do usuário não será atualizada e basicamente parecerá congelada. Se você escreve seu programa usando retornos de chamada, solicita os dados e diz "execute esta função quando terminar". Isso permite que a interface do usuário ainda permita a interação do usuário durante o download do arquivo. Após o download da página, sua função de resultado (retorno de chamada) é chamada e você pode manipular os dados.
Basicamente, ele permite que você solicite algo e continue executando enquanto aguarda o resultado. Depois que o resultado voltar para você por meio de uma função de retorno de chamada, você poderá retomar a operação de onde parou.