Uma função de retorno de chamada é simplesmente uma função que você passa para outra função para que essa função possa chamá-la posteriormente. Isso é comumente visto em APIs assíncronas ; a chamada da API retorna imediatamente porque é assíncrona, então você passa uma função para ela que a API pode chamar quando terminar de executar sua tarefa assíncrona.
O exemplo mais simples que consigo pensar em JavaScript é a setTimeout()
função. É uma função global que aceita dois argumentos. O primeiro argumento é a função de retorno de chamada e o segundo argumento é um atraso em milissegundos. A função foi projetada para aguardar o período de tempo apropriado e, em seguida, invocar sua função de retorno de chamada.
setTimeout(function () {
console.log("10 seconds later...");
}, 10000);
Você pode ter visto o código acima antes, mas simplesmente não percebeu que a função que estava passando era chamada de função de retorno de chamada. Poderíamos reescrever o código acima para torná-lo mais óbvio.
var callback = function () {
console.log("10 seconds later...");
};
setTimeout(callback, 10000);
Callbacks são usados em todo lugar no Node porque o Node é construído desde o início para ser assíncrono em tudo o que faz. Mesmo ao falar com o sistema de arquivos. É por isso que muitas APIs de Nó internas aceitam funções de retorno de chamada como argumentos, em vez de retornar dados que você pode atribuir a uma variável. Em vez disso, ele invocará sua função de retorno de chamada, passando os dados que você deseja como argumento. Por exemplo, você pode usar a fs
biblioteca do Node para ler um arquivo. O fs
módulo expõe duas funções de API exclusivas: readFile
e readFileSync
.
A readFile
função é assíncrona, enquanto readFileSync
obviamente não é. Você pode ver que eles pretendem que você use as chamadas assíncronas sempre que possível, uma vez que as chamaram readFile
e em readFileSync
vez de readFile
e readFileAsync
. Aqui está um exemplo do uso de ambas as funções.
Síncrono:
var data = fs.readFileSync('test.txt');
console.log(data);
O código acima bloqueia a execução do thread até que todo o conteúdo test.txt
seja lido na memória e armazenado na variável data
. No nó, isso é normalmente considerado uma prática ruim. Porém, há momentos em que é útil, como ao escrever um pequeno script rápido para fazer algo simples, mas tedioso e você não se importa muito em economizar cada nanossegundo de tempo que puder.
Assíncrono (com retorno de chamada):
var callback = function (err, data) {
if (err) return console.error(err);
console.log(data);
};
fs.readFile('test.txt', callback);
Primeiro, criamos uma função de retorno de chamada que aceita dois argumentos err
e data
. Um problema com funções assíncronas é que se torna mais difícil detectar erros, portanto, muitas APIs de estilo de retorno de chamada passam os erros como o primeiro argumento para a função de retorno de chamada. É uma prática recomendada verificar se err
tem um valor antes de fazer qualquer outra coisa. Em caso afirmativo, pare a execução do retorno de chamada e registre o erro.
As chamadas síncronas têm uma vantagem quando há exceções lançadas porque você pode simplesmente capturá-las com um try/catch
bloco.
try {
var data = fs.readFileSync('test.txt');
console.log(data);
} catch (err) {
console.error(err);
}
Em funções assíncronas, não funciona assim. A chamada API retorna imediatamente assim não há nada a captura com o try/catch
. APIs assíncronas adequadas que usam retornos de chamada sempre capturarão seus próprios erros e, em seguida, passarão esses erros para o retorno de chamada, onde você pode manipulá-los conforme achar necessário.
Além dos retornos de chamada, existe outro estilo popular de API comumente usado, chamado de promessa. Se você gostaria de ler sobre eles, você pode ler todo o post do blog que escrevi com base nesta resposta aqui .