Programação Assíncrona - JavaScript
Na execução padrão do JavaScript, as instruções são executadas sequencialmente, uma após a outra. Ou seja, primeiro a primeira instrução é executada, depois a segunda e assim por diante. No entanto, o que acontece se uma dessas operações levar um tempo considerável? Por exemplo, ao realizar um trabalho de alta carga, como uma solicitação de rede ou acesso a um banco de dados, o que pode levar um tempo indefinido e, às vezes, prolongado. Como resultado, na execução sequencial, todas as operações subsequentes esperarão pela conclusão dessa operação. Para evitar essa situação, o JavaScript permite o uso de funções assíncronas.
Por exemplo, vamos definir uma simples função assíncrona que emula um trabalho demorado usando a chamada setTimeout()
function asyncFunction() {
setTimeout(()=> {
let result = 22;
console.log("result:", result);
}, 1000);
}
asyncFunction();
console.log("Fim do programa");
Em vez de setTimeout()
Fim do programa result: 22
Aqui vemos que a função assíncrona não bloqueia a execução das demais instruções do programa. No entanto, ao trabalhar com essas funções, podemos enfrentar alguns problemas. Por exemplo, funções assíncronas não retornam o resultado do cálculo assíncrono usando a palavra-chave return
function asyncFunction() {
let result;
setTimeout(()=>{result = 22;}, 1000);
return result;
}
const asyncResult = asyncFunction();
console.log("result:", asyncResult) // result: undefined
Aqui a função assíncrona asyncFunction
asyncResult
asyncFunction
Outro problema está relacionado ao lançamento de exceções com o operador throw
function asyncFunction() {
let result;
setTimeout(()=>{
result = 22;
if(result < 50) {
throw new Error("Valor incorreto");
}
}, 1000);
return result;
}
try {
const asyncResult = asyncFunction();
console.log("result:", asyncResult)
}
catch(error) {
console.error("Error:", error); // Esta linha NÃO será executada
}
console.log("Fim do programa");
Aqui, a manipulação de erros no bloco catch
Inicialmente, o tratamento de resultados e erros em funções assíncronas era feito utilizando funções de callback, que eram passadas para outra função e chamadas posteriormente em algum momento. Um padrão simples de uso de callbacks:
function asyncFunction(callback) {
console.log("Antes da chamada do callback");
callback();
console.log("Depois da chamada do callback");
}
function callbackFunc() {
console.log("Chamada do callback");
}
asyncFunction(callbackFunc);
Aqui, a função asyncFunction
Por exemplo, usamos um callback para obter e processar o resultado e o erro de uma função assíncrona:
function handleResult(error, result) {
if(error) { // se foi passado um erro
console.error(error);
}
else { // se a função assíncrona foi bem-sucedida
console.log("Result:", result);
}
}
function asyncFunction(callback) {
setTimeout(()=> {
let result = Math.floor(Math.random() * 100) + 1;
if(result < 50) {
// se for menor que 50, definimos um erro
callback(new Error("Valor incorreto: " + result), null);
}
else {
// nos outros casos, definimos o resultado
callback(null, result);
}
}, 1000);
}
asyncFunction(handleResult);
A função handleResult
asyncFunction
asyncFunction(handleResult);
Utilizamos o método Math.random()
let result = Math.floor(Math.random() * 100) + 1;
Se o número for menor que 50, um erro é gerado, caso contrário, o número é retornado como resultado.
if(result < 50) {
// se for menor que 50, definimos um erro
callback(new Error("Valor incorreto: " + result), null);
}
Nos outros casos, definimos o resultado e passamos null
else { // nos outros casos, definimos o resultado callback(null, result); }
Exemplo da saída no console para o processamento bem-sucedido (quando o número gerado é igual ou maior que 50):
Result: 70
Se o número gerado for menor que 50, será exibido um erro:
Error: Error: Valor incorreto: 22
Esta é a forma clássica de usar callbacks para processar o resultado de uma função assíncrona. No entanto, ela tem pelo menos uma grande desvantagem: o uso excessivo de funções de callback pode levar à criação de uma estrutura de código conhecida entre os desenvolvedores JavaScript como "callback hell" (inferno dos callbacks). Essa estrutura de código surge quando um callback em uma função assíncrona chama outra função assíncrona, cujo callback, por sua vez, pode chamar uma terceira função assíncrona e assim por diante. Um exemplo dessa estrutura:
asyncFunction((error, result) => {
asyncFunction2((error2, result2) => {
asyncFunction3((error3, result3) => {
asyncFunction4((error4, result4) => {
// algum código
});
});
});
});
Para resolver esse problema, a partir do padrão ES2015, o JavaScript adicionou suporte a Promises, que serão discutidas no próximo tema.