Quem controla o passado dirige o futuro. Quem dirige o futuro conquista o passado. - George Orwell
Uma função callback é essencialmente um padrão(uma solução estabelecida para um problema conhecido), portanto seu uso também atende pelo nome Callback Pattern.
Sua utilização como padrão tem os seguintes objetivos:
- Não repetir código (DRY - Do not Repeat Yourself)
- Proporcionar mais fácil manutenção ao código
- Tornar seu código mais limpo
- Possibilitar uso de funções mais especializadas.
Sendo comumente utilizado nas seguintes situações do desenvolvimento web:
- Execução Assíncrona(Ler arquivos, solicitações HTTP, AJAX);
- Event Listeners;
- Nos métodos setTimeOut e setInterval;
- Criação de APIs, generalização de código;
Events
Eventos indicam quando um particular momento de interesse na janela de seu navegador ocorre. Os interessados naquele particular evento são chamados de subscribers. Cada elemento html na janela de um website como botões,parágrafos,imagens ou qualquer outro que faz parte do DOM são potenciais alvos de um evento.
Para que esses elementos se tornem alvos de um evento, entretanto, é preciso especificar como eles lidarão com a chegada desses "momentos especiais", por exemplo, um "clique". Isso é feito adicionando "ouvidos" a esses elementos, daí vem o nome "Listener". Como os Listeners são os responsáveis por lidarem com o evento, eles também são conhecidos por "EventHandlers".
Como eventos podem ou não ocorrer, o browser(através de um mecanismo de thread única chamado event loops ) fica em constante alerta aos eventos gerados. Quando um clique(evento) ocorre em um botão(alvo) e a este tiver sido atribuído uma maneira de lidar com essa situação(Event Handlers ou Listeners), então haverá uma resposta que "trará de volta" ao usuário um certo resultado.
Devido a essa capacidade de existir antes da ocorrência de um evento, mas ser "chamado de volta" quando o momento certo chegar, que os EventHandlers também são chamados de Callbacks.
Vejamos o simples exemplo abaixo:
var callMeBackFromTheFutureEvent = function(event) {
alert("Here is my answer!")
}
//Elemento DOM
var button = document.getElementById("buttonId");
//Adicionando Listener, no evento click
button.addEventListener("click", callMeBackFromTheFutureEvent );
Criação de APIs
Apesar de o nome "Callback" fazer mais sentido ao falar de eventos, ele é usado em qualquer contexto em que se passa funções como argumentos de uma outra função. Um deles é na criação de APIs(application-programming interface).
Tome, por exemplo, o método nativo filter de Arrays e sua especificação no site do Mozilla:
arr.filter(callback[, thisArg])
Sabendo que um array pode conter ilimitadas maneiras de ser filtrado, a própria linguagem disponibilizou uma API em que o programador deve especificar a maneira por qual deseja realizar o filtro. Assim, a API do Javascript ao retardar a escolha do filtro, conferiu maior flexibilidade e evitou repetição de código.
Vejamos outro exemplo da importância de Callbacks na criação de APIs.
Imagine, por um instante, que você precisa mudar para vermelho a cor da borda de todos os elementos input que não foram preenchidos ao clicar no botão enviar. Uma possível implementação seria:
Apesar dessa implementação funcionar, ela possui algumas deficiências:
- Cria uma variável "inputs" no escopo global;
- É difícil de ser testada, pois depende do clique na interface do usuário;
- Possui serventia muito específica. Por um critério qualquer que vai além do poder do desenvolvedor, a decisão de escolher filtrar inputs vazios poderá mudar para inputs de valores pares. Ou o estilo poderá mudar para azul. Ou até mesmo os elementos a serem verificados podem mudar. Estamos verificando inputs, mas podemos alterar o estilo de botões caso desejemos.
Uma melhor solução seria:
- Criar uma função que filtre os elementos DOM que atendem determinada condição;
- Criar uma função que aplique estilo a elementos;
- Criar uma função que percorra vários elementos;
Dessa forma, de posse das funções setCSS, forEachDomElements e filterDomElements, não haveria problema em mudar o critério de filtro dos elementos tampouco seria difícil alterar o estilo dos elementos filtrados. Experimente fazer uma outra regra com o botão Enviar2.
Métodos setTimeOut e setInterval
Os métodos nativos do objeto global Window, setTimeout e setInterval, fazem uso direto de callbacks em suas estruturas. Isso porque, no caso do setTimeout, a janela não sabe que ação você quer iniciar após um certo intervalo de tempo. E, no caso do setInterval, a janela não sabe que ação você quer executar periodicamente em um dado intervalo de tempo.
Sendo, assim, novamente a API javascript dos browsers(BOM - Browser Object Model), delega a escolha da ação ao desenvolvedor.
Uma das aplicações mais conhecidas destes métodos é a criação de efeitos. Quando você utiliza os efeitos "fadeIn" e "fadeOut" em JQuery, você pode ter certeza que esse métodos estão envolvidos.
É comum também utilizá-los para criação de pollings que são requisições periódicas a dispositivos externos, como servidores. Outra utilização é para definir um limite de tempo de uma ação. Caso ela demore mais que o previsto, você a interrompe.
Aqui, entretanto, criarei uma implementação um tanto rústica do método .on() da API do JQuery.
Esse método é interessante, pois é capaz de adicionar eventHandlers a elementos DOM ainda não criados. Como é possível adicioná-los a elementos que nem sequer existem?
É simples, você fica em constante alerta esperando o elemento existir. Quando ele der o primeiro sinal de vida você instantaneamente acopla um "Listener" a ele.
Imagine, por um instante, que você está criando uma página de cadastros. O cadastro possui uma lista de categorias para você selecionar, mas somente uma delas vai adicionar campos novos a serem preenchidos, e eles precisarão ter alguns eventos atrelados a eles, por exemplo, validações. Neste caso você precisa adicionar EventHandlers aos campos antes inexistentes e é aí que o método .on se torna útil.
É verdade que a renderização de novos campos poderiam ser feitos no próprio cliente, mas se viessem do servidor via uma solicitação AJAX, como adicionar listeners a eles? É aí que o método .on se torna especialmente útil.
Feito o preâmbulo teórico, vamos a implementação rústica do método .on em Javascript aplicada a uma situação simplificada:
Note que na implementação da função onElementExistsAddEvent se faz uso do método setTimeout com o tempo definido de 0ms. Se a nova verificação supostamente é para acontecer de maneira praticamente contínua, porque simplesmente não invocamos a função verifies recursivamente na linha 6?
Experimente e veja o que acontece. Você vai notar que o stack da memória vai chegar ao limite e nenhuma ação vai ocorrer. E por que não acontece isso com setTimeout(f,0)? Porque ele limpa a pilha de chamadas. Isso é esperado uma vez que setTimeout é executado após verifies na fila da UI thread. Veja aqui no stackoverflow um exemplo de como uma nova callstack é criada. Lá haverá um exemplo no JSFiddle que, através de uma análise no console, lhe mostrará justamente isso.
Execução Assíncrona e AJAX
As solicitações AJAX, não possuem nada de especial na sua maneira de lidar com eventos. A única diferença é que o objeto XMLHttpRequest atende a eventos como "readystatechange, load, error" que são eventos específicos de solicitações http.
Não vou entrar em maiores detalhes, pois pretendo abordar AJAX em um tópico separado(Saiba Tudo Sobre AJAX em JavaScript Puro), mas, por enquanto, mantenha em mente que no exemplo em jQuery a seguir, success nada mais é que um callback que responde a um evento, assim como qualquer outro.
$.ajax({
url : 'example.com',
type: 'GET',
success : handleData
})
Conclusão
Callback é um conceito essencial para programadores JavaScript e é a razão principal para a linguagem se moldar tão bem a solicitações assíncronas.
Não importa se você é um desenvoveldor front-end ou trabalha com Node.js, saiba tudo sobre callbacks em JavaScript. Eles serão fundamentais.
Próximos Passos
- Domine Javascript "this" com 3 Exemplos