JavaScript Assíncrono
Se você está se aventurando pelo mundo Front-End, é bem provável que já tenha ouvido falar em JavaScript assíncrono.
Quando comecei a estudar programação seriamente, tive dificuldade em entender JavaScript. No começo, ele é confuso mesmo, mas conforme você vai entendendo os fundamentos por trás da linguagem, ela vai se tornando mais clara e você começa a ter a visão do porque algumas coisas funcionam como funcionam.
Um desafio que eu tive no começo foi entender a parte assíncrona do JavaScript, e, sei que muita gente tem essa dificuldade, por isso estou escrevendo este artigo.
Antes de entrar na linguagem, precisamos entender a diferença entre uma Requisição Síncrona e uma Requisição Assíncrona.
Requisição Síncrona
Como o nome sugere, uma requisição síncrona precisa ter sincronia para funcionar. Isso quer dizer que todas as funções e requisições de um código síncrono trabalham em contato direto, do início ao fim da comunicação.
Exemplo: Se você fizer uma requisição para uma API, você precisa esperar a resposta, até aí ok. Mas se você tiver mais de uma requisição para fazer, você tem que esperar a primeira finalizar.
Ilustrando: Antigamente, navegadores (como Chrome, Firefox, Edge etc) não possuíam o recurso de abas. Então, se você acessasse um site x e quisesse acessar um site y em paralelo, não era possível. Você tinha que sair de um e entrar em outro.
Uma requisição síncrona funciona de forma semelhante, ela bloqueia todas as requisições até que a principal seja finalizada.
Requisição Assíncrona
Ao contrário da anterior, esta não precisa de sincronia para funcionar. Logo, podemos fazer quantas requisições precisarmos sem ter que esperar pela resposta de uma para iniciar outra, e não haverá interferência entre elas.
Usando a ilustração anterior: Hoje os navegadores permitem que várias abas sejam abertas, acessando sites diferentes ao mesmo tempo. Q̶u̶e̶m̶ ̶n̶u̶n̶c̶a̶ ̶s̶e̶ ̶p̶e̶r̶d̶e̶u̶ ̶n̶a̶s̶ ̶a̶b̶a̶s̶?̶
Conceitos explicados, vamos então entender um pouquinho de JavaScript assíncrono.
Existem dois principais estilos de código assíncrono que você encontrará no JavaScript, as callbacks (old-school) e as promises (mais recente).
Callbacks Assíncronas
Callbacks são funções que são passadas como parâmetro na chamada de outra função e que vai executar código por trás do panos. Quando esse código por trás dos panos terminar de ser executado, a função callback será chamada para dizer que a tarefa foi finalizada ou que algo do seu interesse aconteceu. O uso das callbacks é um pouco antiquado agora, mas você ainda pode vê-las em um número de APIs comumente usadas.
Um exemplo de uma callback async é o segundo parâmetro do método addEventListener():
btn.addEventListener('click', () => {
alert('Clicou!');
let pElem = document.createElement('p');
pElem.textContent = 'Este é um novo parágrafo.';
document.body.appendChild(pElem);
});
No exemplo acima, o primeiro parâmetro é o tipo de evento que será executado e o segundo parâmetro é uma função callback que é chamada quando o evento é disparado.
Quando passamos uma função callback como um parâmetro em outra função, nós apenas estamos passando a referência da função como argumento, ou seja, a função callback não é executada imediatamente. Ela é chamada de volta assíncronamente dentro do corpo da função que a contém, que é responsável por executar a função callback quando for necessário.
Callbacks são versáteis, elas permitem controlar a ordem em que as funções são executadas e quais dados são passados entre elas. Elas também podem passar dados para diferentes funções dependendo das circunstâncias.
Nem todas as callbacks são assíncronas — algumas são executadas de um modo síncrono.
Promises:
Promises são uma nova maneira de escrever código assíncrono que você verá em APIs Web modernas. Um bom exemplo disso é a API
fetch()
, que é basicamente uma versão mais moderna e eficiente deXMLHttpRequest
.
fetch('products.json').then(function(response) {
return response.json();
}).then(function(json) {
products = json;
initialize();
}).catch(function(err) {
console.log('Fetch problem: ' + err.message);
});
No exemplo acima, nós vemos fetch()
pegando um único parâmetro — a URL de um recurso que você quer pegar da rede — e retornando uma promise. A promise é um objeto que representa a conclusão ou falha da operação assíncrona. Podemos dizer que ela representa um estado intermediário. É como se o navegador estivesse dizendo: "Eu prometo voltar para você com a resposta", daí o nome "promessa".
Nós temos três blocos de código encadeados ao fim do fetch()
:
- Dois blocos
then()
. Ambos contém uma função callback que será executada se a operação anterior for executada, então você pode fazer algo com o resultado. Cada bloco.then()
retorna outra promise, o que significa que você pode encadear múltiplos blocos.then()
um ao outro, para que múltiplas operações assíncronas possam ser executadas uma atrás da outra. Aqui fica um alerta, tome cuidado ao encadear vários blocos .then(), seu código pode ficar ilegível para quem for dar manutenção! - O bloco
catch()
no final será executado em casos em que erros ocorrem quando um dos.then()
falhe — de um modo similar aos blocostry...catch
síncronos, um objeto de erro fica disponível dentro docatch()
, e pode ser usado para reportar erros que ocorreram. O blocotry...catch
não funcionará com promises, embora funcione com async/await.
Conclusão, ambas as formas funcionam, porém está em alta usar promises :)
Referência: https://developer.mozilla.org.