Opa tudo bem Bruno? Seguinte, eu tecnicamente aprendi async/await na marra sem ordem nem nada, não estou dizendo que é o certo. Portanto, a diferença entre promise e try/catch, funcionalmente são a mesma coisa, mas a promise se torna um pouco diferente.
Try/catch
Esse método, ele é utilizado em casos sincronos onde não há a utilização de async/await, mas nada diz que você precisa utilizar esse método só nessa situação. No exemplo abaixo explico como funciona o resultado da função.
async function hello_world() {
try {
const request = await _api-response_
return request;
} catch (error) {
return error;
}
}
Nessa função, o resultado produzido será um return com os dados esperados - se der certo - ou um resultado com os erros. Se o resultado for o esperado e você, por exemplo receber um json, você conseguirá acessar o json sem precisar realizar outro passo, nesse caso, um .then que é necessário quando se tem uma promise sem async/await.
Promise
Essencialmente a ideia é a mesma que o try/catch, mas esse método é utilizado e recomendado para funções assíncronas como em apis próprias. Normalmente se pode utilizar sem a necessidade async/await, mas você precisará encadear estruturas .then/.catch para saber os resultados.
function minhaFunçao() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Hello");
}, 2000);
});
minhaFuncao()
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error(error);
});
Neste exemplo, se você colocar a função (minhaFuncao) em um console, você perceberá que será mostrado: "Promise {<pending}". Nesse caso você não consegue saber o retorno da função, para saber você precisa realizar a estrutura de .then que mostra o resultado da promise. Entretanto, você pode evitar esse passos a mais utilizando o async/await para dizer ao request que você pode esperar as promises se resolverem e trazer o resultado esperado, sem a necessidade de fazer o .then/.catch.
Quando utilizar um ou outro?
Resumidamente, utiliza-se o método try/catch em situações síncronas que podem gerar erros que você queira tratar. Já a promise é utilizada, comumente, para funções assíncronas. Ou seja, essencialmente são a mesma coisa, são estruturas para tratar resultados diferentes, mas são utilizadas em contextos diferentes, como em contextos síncronos (try/catch) e assíncronos (promises). No fim, você pode utilizar try/catch em situações assícronas e promises em situações sincronas ou vice e versa, mas a utilizacão de promises em contextos assíncronos, torna a leitura do código mais limpa e clara demonstrando organização de código a outros programadores.
Complementando um pouco a resposta do EstevamOt, já que você tá começando a estudar funções assíncronas.
Quando a gente trabalha com Promise e Callbacks a ideia é que estamos avisando pro "código": olha, isso aqui pode demorar um pouco, então ao invés de bloquear a execução de tudo pode ir fazendo o que precisa e quando eu terminar eu te aviso.
Então pegando como exemplo a função:
/**
* Imagina que é uma chamada a alguma API
*
* @returns {Promise<number>}
*/
function pegaIdade() {
return new Promise(function (resolve, reject) {
const idade = Math.floor(Math.random() * 100);
// Caso deu tudo certo
if (idade) {
resolve(idade);
} else {
reject("Idade Inválida");
}
});
}
A função pegaIdade
faria alguma consulta a alguma API e retornaria uma Promise. A Promise aceita 2 funções de callback, uma que é executada quando a operação ocorre com sucesso (resolve
) e outra para quando tem erro na operação (reject
).
A Promise aceita, por meio de uma interface fluente, indicar o que será executado quando terminar. No caso de sucesso executará o then
e no caso de falha pulará direto para o catch
.
pegaIdade()
.then(idade => console.log(idade))
.catch(error => console.error(error))
O bom de usar Promise é que você pode executar todas de uma única vez e então ter só 1 callback pra executar após todas retornarem algo. Muito útil quando a ordem de execução de uma função não depende da outra.
Promise
.all([pegaIdade(), pegaIdade(), pegaIdade()])
.then(([idade1, idade2, idade3]) => {
console.log("idade1: " + idade1);
console.log("idade2: " + idade2);
console.log("idade3: " + idade3);
})
.catch(error => console.error(error))
Promise também permite encadeamento do retorno, para quando você precisa tratar o dado antes de ir pra próxima etapa.
pegaIdade()
.then((idade) => {
if (idade < 18) {
throw "Menores não permitidos"
}
// Esse return jogará uma nova resposta que será executada pelo próximo then
return idade % 2 ? false : true;
})
.then(ePar => console.log(ePar))
.catch(error => console.error(error))
Quando fazemos esse encadeamento de then
é porque uma resposta depende de alguma chamada assíncrona. Mas ir encadeando um monte de then pode deixar o código meio confuso.
Aí entram o async/await marcando a função.
Dentro de uma função marcada com async
podemos indicar que vamos esperar a Promise executar e retornar o seu valor ao invés de aguardar a Promise chamar nossa callback. Isso fazemos com o await
. Desta forma quando temos várias chamadas a funções assíncronas e que uma chamada dependa da anterior fazemos um código mais linear ao invés de vários encadeamentos.
Mas quando fazemos desta forma o erro que a Promise dispara vira uma exceção. Por isso precisamos tratar com try...catch
.
/**
* @returns {Promise<boolean>}
*/
async function reserva() {
try {
const idade = await pegaIdade();
if (idade < 18) {
throw "Menores não permitidos";
}
// Imagina que aqui retorna um boolean
const reservaEfetuada = await efetuaReserva();
return reservaEfetuada
} catch (error) {
console.error(error);
return false;
}
}
Importante também ressaltar que quando marcamos uma função com async
estamos transformando ela numa Promise.