Javascript – Entendendo Callback

Callback é uma função que é usada como “callback”. Ela é tipicamente passada como argumento de outra função e/ou chamada quando um evento for acontecido, ou quando uma parte de código receber uma resposta de que estava à espera.

Isto é muito comum em javascript lado cliente e servidor (NodeJS) mas não só. A função callback é muitas vezes assíncrona na medida em que é chamada como consequência de outro código que esta a correr no background. A grande vantagem é que assim o computador pode ir processando outros processos enquanto a resposta não chega e não precisa assim de parar tudo à espera dessa resposta.

Um exemplo clássico é um auscultador de eventos que chama uma função quando o evento acontece:

function callback(e) {
    alert('Aconteceu um evento ' + e.type);
}
window.addEventListener('click', callback);

Este callback vai ser chamada quando fizer um click na janela. Até isso acontecer o processador vai processando outras coisas que acontecem na aplicação.

Exemplo: https://jsfiddle.net/D2PuH/


Outro exemplo é uma chamada de ajax, que faz um pedido ao servidor e corre a função quando receber a resposta.

Essa função é uma função “callback”. No exemplo em baixo (https://jsfiddle.net/skQr7/) chamei-lhe minhaCallBack.

function minhaCallBack(returnhtml) {
    $("#result").html(returnhtml);
    $("#loadingimg").hide();
}

$(document).ready(function (e) {
        $.ajax({
            url: form_url,
            type: form_method,
            data: form_data,
            cache: false,
            success: minhaCallBack
        }); 
});

Outro exemplo ainda (https://jsfiddle.net/V3wDF/) é uma animação que chama a callback no fim da animação estar completa:

Vou fazer uma analogia entre código síncrono e código assíncrono.
Imaginando esta situação:

Problema:

Como transformar um numero no formato string, com parte decimal longa demais que deve ser convertido em dólares, e verificar se um dado cliente têm direito a desconto.

Solução síncrona:

Para resolver este problema de forma síncrona temos de saber o valor inicial e o valor do câmbio antes de iniciarmos o cálculo. Isto nem sempre é assim, mas para o exemplo vamos assumir que sabemos o seguinte:

var valorInicial = "1003,087293487";
var descontoCliente = "5%";
var cambio =  1.1158; // 1 EUR = 1.11580USD

assim podemos fazer a transformação de maneira simples:

var valorInicialEmFormatoNumerico = Number(valorInicial.replace(',', '.'));
var valorComDescontoAplicado = valorInicialEmFormatoNumerico - (valorInicialEmFormatoNumerico * parseFloat(descontoCliente) / 100);
var valorEmDolares = valorComDescontoAplicado * cambio;
var totalComDuasCasasDecimais = valorEmDolares.toFixed(2);

Assim o resultado é obtido com passos síncronos, e dá 1063.28

Solução assíncrona:

Imagina agora que precisamos de fazer a mesma coisa, mas não temos a informação toda disponível, pois os câmbios mudam constantemente, não podemos ter disponível o desconto de todos os possíveis clientes, e nem mesmo o preço inicial pois temos de perguntar ao nosso fornecedor primeiro.

Como fazer?

Neste caso temos de fazer a coisa assíncrona, ou seja correndo parte do código, esperar por respostas, correr mais um pedaço e esperar por outra resposta, etc…

Podemos dizer que quando perguntamos algo a um serviço exterior fazemos provavelmente um pedido ajax. E agora precisamos de uma ferramenta para receber a resposta, é aí que callbacks entram em jogo.

Quando envio uma pergunta para um serviço externo tenho de lhe dizer como ele me deve contactar/informar com o resultado. Uma maneira comum é enviar uma função com o pedido para que ela seja corrida quando a resposta chegar.

enviar uma função com o pedido para que ela seja corrida quando a resposta chegar

Se eu enviar uma função a um setTimeout, digo ao setTimeout qual o código a correr quando o tempo tiver esgotado. Essa função pode ter o nome que quisermos, mas a sua funcionalidade, o seu tipo é callback:

var fazerAlgo = function(){ alert('O tempo chegou ao fim!'); }
setTimeout(fazerAlgo, 2000); // correr ao fim de 2 segundos

Voltando ao exemplo das contas:

Para fazermos o cálculo anterior com lógica assíncrona temos de encadear as coisas e depender de funções que vão sendo chamadas com tempos de espera entre elas. Assim temos de encadear tudo, por exemplo assim:

pedirPrecoAoFornecedor(numeroProduto, function(respostaDoFornecedor) {
  var valorInicialEmFormatoNumerico = Number(respostaDoFornecedor.replace(',', '.'));
  pedirInformacaoDesconto(numeroCliente, function(descontoCliente) {
    var valorComDescontoAplicado = valorInicialEmFormatoNumerico - (valorInicialEmFormatoNumerico * parseFloat(descontoCliente) / 100);
    pedirCambioDaHora('USD', function(valorDoDolar) {
      var valorEmDolares = valorComDescontoAplicado * valorDoDolar;
      var totalComDuasCasasDecimais = valorEmDolares.toFixed(2);

      // !! agora temos o resultado!! 
      alert(totalComDuasCasasDecimais + 'USD');

    })
  })
})

Callback é uma função que é usada como “callback”. Ela é tipicamente passada como argumento de outra função e/ou chamada quando um evento for acontecido, ou quando uma parte de código receber uma resposta de que estava à espera.

Isto é muito comum em javascript lado cliente e servidor (NodeJS) mas não só. A função callback é muitas vezes assíncrona na medida em que é chamada como consequência de outro código que esta a correr no background. A grande vantagem é que assim o computador pode ir processando outros processos enquanto a resposta não chega e não precisa assim de parar tudo à espera dessa resposta.

Um exemplo clássico é um auscultador de eventos que chama uma função quando o evento acontece:

function callback(e) {
    alert('Aconteceu um evento ' + e.type);
}
window.addEventListener('click', callback);

Este callback vai ser chamada quando fizer um click na janela. Até isso acontecer o processador vai processando outras coisas que acontecem na aplicação.

Exemplo: https://jsfiddle.net/D2PuH/


Outro exemplo é uma chamada de ajax, que faz um pedido ao servidor e corre a função quando receber a resposta.

Essa função é uma função “callback”. No exemplo em baixo (https://jsfiddle.net/skQr7/) chamei-lhe minhaCallBack.

function minhaCallBack(returnhtml) {
    $("#result").html(returnhtml);
    $("#loadingimg").hide();
}

$(document).ready(function (e) {
        $.ajax({
            url: form_url,
            type: form_method,
            data: form_data,
            cache: false,
            success: minhaCallBack
        }); 
});

Outro exemplo ainda (https://jsfiddle.net/V3wDF/) é uma animação que chama a callback no fim da animação estar completa:

function completa() {
    alert('Este alert está dentro da função "callback"');
}
$('div').animate({
    'height': '200px'
}, {
    duration: 2000,
    complete: completa
});

Outro exemplo ainda, para tentar clarificar mais depois de surgir mais uma pergunta sobre o assunto.

Vou fazer uma analogia entre código síncrono e código assíncrono.
Imaginando esta situação:

Problema:

Como transformar um numero no formato string, com parte decimal longa demais que deve ser convertido em dólares, e verificar se um dado cliente têm direito a desconto.

Solução síncrona:

Para resolver este problema de forma síncrona temos de saber o valor inicial e o valor do câmbio antes de iniciarmos o cálculo. Isto nem sempre é assim, mas para o exemplo vamos assumir que sabemos o seguinte:

var valorInicial = "1003,087293487";
var descontoCliente = "5%";
var cambio =  1.1158; // 1 EUR = 1.11580USD

assim podemos fazer a transformação de maneira simples:

var valorInicialEmFormatoNumerico = Number(valorInicial.replace(',', '.'));
var valorComDescontoAplicado = valorInicialEmFormatoNumerico - (valorInicialEmFormatoNumerico * parseFloat(descontoCliente) / 100);
var valorEmDolares = valorComDescontoAplicado * cambio;
var totalComDuasCasasDecimais = valorEmDolares.toFixed(2);

Assim o resultado é obtido com passos síncronos, e dá 1063.28

Solução assíncrona:

Imagina agora que precisamos de fazer a mesma coisa, mas não temos a informação toda disponível, pois os câmbios mudam constantemente, não podemos ter disponível o desconto de todos os possíveis clientes, e nem mesmo o preço inicial pois temos de perguntar ao nosso fornecedor primeiro.

Como fazer?

Neste caso temos de fazer a coisa assíncrona, ou seja correndo parte do código, esperar por respostas, correr mais um pedaço e esperar por outra resposta, etc…

Podemos dizer que quando perguntamos algo a um serviço exterior fazemos provavelmente um pedido ajax. E agora precisamos de uma ferramenta para receber a resposta, é aí que callbacks entram em jogo.

Quando envio uma pergunta para um serviço externo tenho de lhe dizer como ele me deve contactar/informar com o resultado. Uma maneira comum é enviar uma função com o pedido para que ela seja corrida quando a resposta chegar.

enviar uma função com o pedido para que ela seja corrida quando a resposta chegar

Se eu enviar uma função a um setTimeout, digo ao setTimeout qual o código a correr quando o tempo tiver esgotado. Essa função pode ter o nome que quisermos, mas a sua funcionalidade, o seu tipo é callback:

var fazerAlgo = function(){ alert('O tempo chegou ao fim!'); }
setTimeout(fazerAlgo, 2000); // correr ao fim de 2 segundos

Voltando ao exemplo das contas:

para fazermos o cálculo anterior com lógica assíncrona temos de encadear as coisas e depender de funções que vão sendo chamadas com tempos de espera entre elas. Assim temos de encadear tudo, por exemplo assim:

pedirPrecoAoFornecedor(numeroProduto, function(respostaDoFornecedor) {
  var valorInicialEmFormatoNumerico = Number(respostaDoFornecedor.replace(',', '.'));
  pedirInformacaoDesconto(numeroCliente, function(descontoCliente) {
    var valorComDescontoAplicado = valorInicialEmFormatoNumerico - (valorInicialEmFormatoNumerico * parseFloat(descontoCliente) / 100);
    pedirCambioDaHora('USD', function(valorDoDolar) {
      var valorEmDolares = valorComDescontoAplicado * valorDoDolar;
      var totalComDuasCasasDecimais = valorEmDolares.toFixed(2);

      // !! agora temos o resultado!! 
      alert(totalComDuasCasasDecimais + 'USD');

    })
  })
})

Este encadeamento de funções é necessário pois cada callback será chamada quando o serviço externo quiser. Por exemplo a função pedirPrecoAoFornecedor recebe dois argumentos: o numero do produto e uma função. Como eu sei que o que vou receber é o preço do produto coloco já isso como nome da variável, para facilitar a leitura. Essa função que passo a pedirPrecoAoFornecedor é portanto uma callback, um mecanismo para o serviço externo me poder devolver o que preciso.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *