Retornos de chamada JavaScript

Resumo : neste tutorial, você aprenderá sobre funções de retorno de chamada JavaScript, incluindo retornos de chamada síncronos e assíncronos.

O que são retornos de chamada

Em JavaScript, as funções são cidadãs de primeira classe . Portanto, você pode passar uma função para outra função como argumento.

Por definição, um retorno de chamada é uma função que você passa para outra função como argumento para execução posterior.

O seguinte define uma filter()função que aceita um array de números e retorna um novo array de números ímpares:

function filter(numbers) {
  let results = [];
  for (const number of numbers) {
    if (number % 2 != 0) {
      results.push(number);
    }
  }
  return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];
console.log(filter(numbers));Linguagem de código:  JavaScript  ( javascript )

Como funciona.

  • Primeiro, defina a filter()função que aceita um array de números e retorna um novo array de números ímpares.
  • Em segundo lugar, defina a numbersmatriz que contém números pares e ímpares.
  • Terceiro, chame a filter()função para obter os números ímpares da matriz de números e gerar o resultado.

Se quiser retornar um array que contém números pares, você precisa modificar a filter()função. Para tornar a filter()função mais genérica e reutilizável, você pode:

  • Primeiro, extraia a lógica do ifbloco e envolva-a em uma função separada.
  • Segundo, passe a função para a função filter() como argumento.

Aqui está o código atualizado:

function isOdd(number) {
  return number % 2 != 0;
}

function filter(numbers, fn) {
  let results = [];
  for (const number of numbers) {
    if (fn(number)) {
      results.push(number);
    }
  }
  return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];
console.log(filter(numbers, isOdd));Linguagem de código:  JavaScript  ( javascript )

O resultado é o mesmo. Entretanto, você pode passar qualquer função que aceite um argumento e retorne um valor booleano para o segundo argumento da filter()função.

Por exemplo, você pode usar a filter()função para retornar uma matriz de números pares como esta:

function isOdd(number) {
  return number % 2 != 0;
}
function isEven(number) {
  return number % 2 == 0;
}

function filter(numbers, fn) {
  let results = [];
  for (const number of numbers) {
    if (fn(number)) {
      results.push(number);
    }
  }
  return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];

console.log(filter(numbers, isOdd));
console.log(filter(numbers, isEven));Linguagem de código:  JavaScript  ( javascript )

Por definição, isOdde isEvensão funções de retorno de chamada ou retornos de chamada. Como a filter()função aceita uma função como argumento, ela é chamada de função de ordem superior .

Um retorno de chamada pode ser uma função anônima, que é uma função sem nome como este:

function filter(numbers, callback) {
  let results = [];
  for (const number of numbers) {
    if (callback(number)) {
      results.push(number);
    }
  }
  return results;
}

let numbers = [1, 2, 4, 7, 3, 5, 6];

let oddNumbers = filter(numbers, function (number) {
  return number % 2 != 0;
});

console.log(oddNumbers);Linguagem de código:  JavaScript  ( javascript )

Neste exemplo, passamos uma função anônima para a filter()função em vez de usar uma função separada.

No ES6, você pode usar uma função de seta como esta:

function filter(numbers, callback) {
  let results = [];
  for (const number of numbers) {
    if (callback(number)) {
      results.push(number);
    }
  }
  return results;
}

let numbers = [1, 2, 4, 7, 3, 5, 6];

let oddNumbers = filter(numbers, (number) => number % 2 != 0);

console.log(oddNumbers);Linguagem de código:  JavaScript  ( javascript )

Existem dois tipos de retornos de chamada: retornos de chamada síncronos e assíncronos.

Retornos de chamada síncronos

Um retorno de chamada síncrono é executado durante a execução da função de ordem superior que usa o retorno de chamada. Os isOdde isEvensão exemplos de retornos de chamada síncronos porque são executados durante a execução da filter()função.

Retornos de chamada assíncronos

Um retorno de chamada assíncrono é executado após a execução da função de ordem superior que usa o retorno de chamada.

Assincronicidade significa que se o JavaScript tiver que esperar a conclusão de uma operação, ele executará o restante do código enquanto espera.

Observe que JavaScript é uma linguagem de programação de thread único. Ele realiza operações assíncronas por meio da fila de retorno de chamada e do loop de eventos .

Suponha que você precise desenvolver um script que baixe uma imagem de um servidor remoto e a processe após a conclusão do download:

function download(url) {
    // ...
}

function process(picture) {
    // ...
}

download(url);
process(picture);Linguagem de código:  JavaScript  ( javascript )

No entanto, o download de uma imagem de um servidor remoto leva tempo, dependendo da velocidade da rede e do tamanho da imagem.

A download()função a seguir usa a setTimeout()função para simular a solicitação de rede:

function download(url) {
    setTimeout(() => {
        // script to download the picture here
        console.log(`Downloading ${url} ...`);
    },1000);
}Linguagem de código:  JavaScript  ( javascript )

E este código emula a process()função:

function process(picture) {
    console.log(`Processing ${picture}`);
}Linguagem de código:  JavaScript  ( javascript )

Quando você executa o seguinte código:

let url = 'https://tutorials.acervolima.com/pic.jpg';

download(url);
process(url);Linguagem de código:  JavaScript  ( javascript )

você obterá a seguinte saída:

Processing https://tutorials.acervolima.com/pic.jpg
Downloading https://tutorials.acervolima.com/pic.jpg ...Linguagem de código:  JavaScript  ( javascript )

Isso não é o que você esperava porque a process()função é executada antes da download()função. A sequência correta deve ser:

  • Baixe a imagem e aguarde o download ser concluído.
  • Processe a imagem.

Para resolver esse problema, você pode passar a process()função para a download()função e executar a process()função dentro da download()função assim que o download for concluído, assim:

function download(url, callback) {
    setTimeout(() => {
        // script to download the picture here
        console.log(`Downloading ${url} ...`);
        
        // process the picture once it is completed
        callback(url);
    }, 1000);
}

function process(picture) {
    console.log(`Processing ${picture}`);
}

let url = 'https://tutorials.acervolima.com/pic.jpg';
download(url, process);Linguagem de código:  JavaScript  ( javascript )

Saída:

Downloading https://tutorials.acervolima.com/pic.jpg ...
Processing https://tutorials.acervolima.com/pic.jpgLinguagem de código:  JavaScript  ( javascript )

Agora, funciona conforme o esperado.

Neste exemplo, process()é um retorno de chamada passado para uma função assíncrona.

Quando você usa um retorno de chamada para continuar a execução do código após uma operação assíncrona, o retorno de chamada é chamado de retorno de chamada assíncrono.

Para tornar o código mais conciso, você pode definir a process()função como anônima:

function download(url, callback) {
    setTimeout(() => {
        // script to download the picture here
        console.log(`Downloading ${url} ...`);
        // process the picture once it is completed
        callback(url);

    }, 1000);
}

let url = 'https://tutorials.acervolima.com/pic.jpg';
download(url, function(picture) {
    console.log(`Processing ${picture}`);
}); Linguagem de código:  JavaScript  ( javascript )

Tratamento de erros

A download()função assume que tudo funciona bem e não considera nenhuma exceção. O código a seguir apresenta dois retornos de chamada: successe failurepara lidar com os casos de sucesso e falha, respectivamente:

function download(url, success, failure) {
  setTimeout(() => {
    console.log(`Downloading the picture from ${url} ...`);
    !url ? failure(url) : success(url);
  }, 1000);
}

download(
  '',
  (url) => console.log(`Processing the picture ${url}`),
  (url) => console.log(`The '${url}' is not valid`)
);
Linguagem de código:  JavaScript  ( javascript )

Aninhando retornos de chamada e a Pirâmide da Perdição

Como você baixa três imagens e as processa sequencialmente? Uma abordagem típica é chamar a download()função dentro da função de retorno de chamada, assim:

function download(url, callback) {
  setTimeout(() => {
    console.log(`Downloading ${url} ...`);
    callback(url);
  }, 1000);
}

const url1 = 'https://tutorials.acervolima.com/pic1.jpg';
const url2 = 'https://tutorials.acervolima.com/pic2.jpg';
const url3 = 'https://tutorials.acervolima.com/pic3.jpg';

download(url1, function (url) {
  console.log(`Processing ${url}`);
  download(url2, function (url) {
    console.log(`Processing ${url}`);
    download(url3, function (url) {
      console.log(`Processing ${url}`);
    });
  });
});
Linguagem de código:  JavaScript  ( javascript )

Saída:

Downloading https://tutorials.acervolima.com/pic1.jpg ...
Processing https://tutorials.acervolima.com/pic1.jpg
Downloading https://tutorials.acervolima.com/pic2.jpg ...
Processing https://tutorials.acervolima.com/pic2.jpg
Downloading https://tutorials.acervolima.com/pic3.jpg ...
Processing https://tutorials.acervolima.com/pic3.jpgLinguagem de código:  JavaScript  ( javascript )

O script funciona perfeitamente bem.

No entanto, esta estratégia de retorno de chamada não se adapta bem quando a complexidade aumenta significativamente.

Aninhar muitas funções assíncronas dentro de retornos de chamada é conhecido como pirâmide da destruição ou inferno de retorno de chamada :

asyncFunction(function(){
    asyncFunction(function(){
        asyncFunction(function(){
            asyncFunction(function(){
                asyncFunction(function(){
                    ....
                });
            });
        });
    });
});
Linguagem de código:  JavaScript  ( javascript )

Para evitar a pirâmide da destruição, você usa promessas ou funções assíncronas/esperadas .

Resumo

  • Um retorno de chamada é uma função passada para outra função como um argumento a ser executado posteriormente.
  • Uma função de ordem superior é uma função que aceita outra função como argumento.
  • As funções de retorno de chamada podem ser síncronas ou assíncronas.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *