Promises
Esta aula explora Promises em JavaScript, um padrão fundamental para lidar com operações assíncronas. Você aprenderá sobre os estados de uma Promise, os métodos then/catch/finally, encadeamento de Promises e como criar suas próprias Promises.
Promises são objetos que representam a eventual conclusão (ou falha) de uma operação assíncrona e seu valor resultante. Elas fornecem uma maneira mais limpa e legível de lidar com código assíncrono, evitando o famoso "callback hell". Uma Promise pode estar em um de três estados: pendente (pending), resolvida (fulfilled) ou rejeitada (rejected).
Nesta aula, vamos cobrir os conceitos essenciais de Promises, incluindo seus estados, métodos de tratamento (then, catch, finally), como encadear múltiplas Promises e como criar suas próprias Promises a partir do zero.
Estados
Uma Promise possui três estados mutuamente exclusivos:
- Pending (pendente): estado inicial, a Promise ainda não foi resolvida nem rejeitada.
- Fulfilled (resolvida): a operação assíncrona foi concluída com sucesso, e a Promise possui um valor.
- Rejected (rejeitada): a operação assíncrona falhou, e a Promise possui uma razão (erro).
Uma vez que uma Promise é resolvida ou rejeitada, ela se torna imutável: seu estado e valor não podem ser alterados. Isso garante previsibilidade no fluxo assíncrono.
Exemplo visualizando estados:
const promise = new Promise((resolve, reject) => {
// estado pending
setTimeout(() => {
resolve('Sucesso!'); // muda para fulfilled
// reject('Erro'); // mudaria para rejected
}, 1000);
});
console.log(promise); // Promise { <pending> }
then/catch/finally
Os métodos then, catch e finally são usados para reagir ao resultado de uma Promise.
then(onFulfilled, onRejected): Retorna uma nova Promise. O primeiro callback é executado quando a Promise é resolvida, o segundo (opcional) quando é rejeitada.
const promise = Promise.resolve(42);
promise.then(
value => console.log('Resolvida com', value),
reason => console.log('Rejeitada por', reason)
);
// Saída: Resolvida com 42
catch(onRejected): É uma abreviação para .then(undefined, onRejected). Captura rejeições que ocorreram em qualquer ponto anterior da cadeia.
Promise.reject('Erro')
.catch(error => console.log('Capturado:', error));
// Saída: Capturado: Erro
finally(onFinally): Executa um callback independentemente do resultado (resolvida ou rejeitada). Útil para limpeza, como fechar conexões. Não recebe argumentos e não altera o valor da Promise.
Promise.resolve('OK')
.finally(() => console.log('Finalizado'))
.then(value => console.log('Valor:', value));
// Saída: Finalizado
// Valor: OK
Encadeamento
O encadeamento de Promises permite executar operações assíncronas sequenciais de forma linear. Cada then retorna uma nova Promise, que pode ser usada para o próximo then.
Exemplo: buscar dados de uma API e processá-los.
fetch('https://api.exemplo.com/dados')
.then(response => response.json())
.then(data => {
console.log('Dados recebidos:', data);
return data.id;
})
.then(id => fetch(`https://api.exemplo.com/detalhes/${id}`))
.then(response => response.json())
.then(detalhes => console.log('Detalhes:', detalhes))
.catch(error => console.error('Erro na cadeia:', error));
Se qualquer Promise na cadeia for rejeitada, o controle pula para o catch mais próximo. É uma boa prática ter um catch no final da cadeia para tratar erros.
Criando uma Promise
Podemos criar nossas próprias Promises usando o construtor Promise, que recebe uma função executor com dois parâmetros: resolve e reject.
const minhaPromise = new Promise((resolve, reject) => {
// Operação assíncrona: setTimeout, leitura de arquivo, etc.
const sucesso = true;
if (sucesso) {
resolve('Operação bem-sucedida');
} else {
reject('Falha na operação');
}
});
minhaPromise
.then(resultado => console.log(resultado))
.catch(erro => console.error(erro));
É comum usar Promises para envolver funções que usam callbacks, como setTimeout ou APIs legadas.
function esperar(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
esperar(2000).then(() => console.log('Passaram-se 2 segundos'));
Importante: a função executor é executada imediatamente (sincronamente) quando a Promise é criada, mas o resolve ou reject são chamados assincronamente.
Boas Práticas
- Sempre retorne Promises em encadeamentos para manter a sequência.
- Adicione um
catchao final de cada cadeia para evitar erros não tratados. - Use
finallypara operações de limpeza (ex.: fechar arquivos, esconder loading). - Prefira
Promise.allpara operações paralelas independentes.
Referências
- MDN: Promise
- JavaScript.info: Promises
- W3Schools: JavaScript Promises
- Web.dev: JavaScript Promises
- Node.js: Using Promises
Exercícios
- Crie uma Promise que seja resolvida após 1 segundo com o valor "Pronto". Use
thenpara exibir o valor no console. - Escreva uma função
dividir(a, b)que retorna uma Promise. Se b for 0, rejeite com "Divisão por zero"; caso contrário, resolva com a/b. - Encadeie duas Promises: a primeira resolve com 5 após 1 segundo, a segunda multiplica o valor por 2. Exiba o resultado final.
- Use
finallypara imprimir "Operação finalizada" independentemente de sucesso ou erro em uma Promise que rejeita com "Erro simulado". - Crie uma função
buscarUsuario(id)que retorna uma Promise que simula uma requisição: se id for par, resolve com {id, nome: "Usuário"+id}; se ímpar, rejeita com "Usuário não encontrado".
const promise = new Promise(resolve => {
setTimeout(() => resolve('Pronto'), 1000);
});
promise.then(value => console.log(value));
function dividir(a, b) {
return new Promise((resolve, reject) => {
if (b === 0) {
reject('Divisão por zero');
} else {
resolve(a / b);
}
});
}
dividir(10, 2).then(console.log).catch(console.error); // 5
dividir(10, 0).then(console.log).catch(console.error); // 'Divisão por zero'
new Promise(resolve => setTimeout(() => resolve(5), 1000))
.then(valor => valor * 2)
.then(resultado => console.log(resultado)); // 10 após 1 segundo
Promise.reject('Erro simulado')
.finally(() => console.log('Operação finalizada'))
.catch(error => console.error(error));
// Saída: Operação finalizada
// Erro simulado
function buscarUsuario(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id % 2 === 0) {
resolve({ id, nome: `Usuário${id}` });
} else {
reject('Usuário não encontrado');
}
}, 500);
});
}
buscarUsuario(2).then(console.log).catch(console.error); // { id: 2, nome: 'Usuário2' }
buscarUsuario(1).then(console.log).catch(console.error); // 'Usuário não encontrado'