JavaScript - Combinadores de Promises

Image for post
Image for post

Desde a introdução de Promises no ES2015, o JavaScript suportou exatamente dois combinadores: os métodos estáticos Promise.all e Promise.race.

Duas novas propostas estão atualmente passando pelo processo de padronização: Promise.allSettled e Promise.any. Com essas adições, haverá um total de quatro combinadores de Promises em JavaScript, cada um permitindo diferentes casos de uso.

Aqui está uma visão geral dos quatro combinadores:

  • ‌Promise.allSettled: não há curto-circuito, em proposta
  • Promise.all: curto-circuito quando um valor de entrada é rejeitado, adicionado no ES2015 ✅
  • Promise.race: curto-circuito quando algum valor de entrada for decidido (rejeitado ou resolvido), adicionado no ES2015 ✅
  • Promise.any: curto-circuito quando um valor de entrada for resolvido, em proposta

Vamos dar uma olhada em um exemplo de caso de uso para cada combinador.

Promise.all

Promise.all permite que você saiba quando todas as promessas de entrada foram cumpridas ou quando uma delas foi rejeitada.

Imagine que o usuário clica em um botão e deseja carregar algumas folhas de estilo para que você possa renderizar uma interface de usuário completamente nova. Este programa inicia uma solicitação HTTP para cada folha de estilo em paralelo:

const promises = [
fetch('/component-a.css'),
fetch('/component-b.css'),
fetch('/component-c.css'),
];
try {
const styleResponses = await Promise.all(promises);
enableStyles(styleResponses);
renderNewUi();
} catch (reason) {
displayError(reason);
}

Você só deseja começar a renderizar a nova interface de usuário depois que todas as solicitações forem bem-sucedidas. Se algo der errado, você deseja exibir uma mensagem de erro o mais rápido possível, sem esperar que outro trabalho acabe.

Nesse caso, você poderia usar Promise.all: você quer saber quando todas as promessas são cumpridas ou assim que uma delas é rejeitada.

Promise.race

Promise.race é útil se você deseja executar várias promessas e também:

  1. fazer algo com o primeiro resultado bem-sucedido que chegar (no caso de uma das promessas ser cumprida), ou
  2. faça algo assim que uma das promessas for rejeitada.

Isto é, se uma das promessas for rejeitada, você quer preservar essa rejeição para tratar o caso de erro separadamente. O exemplo a seguir faz exatamente isso:

try {
const result = await Promise.race([
performHeavyComputation(),
rejectAfterTimeout(2000),
]);
renderResult(result);
} catch (error) {
renderError(error);
}

Iniciamos uma tarefa computacionalmente pesada que pode levar muito tempo, mas corremos contra uma promessa que rejeita após 2 segundos. Dependendo da primeira promessa a ser cumprida ou rejeitada, renderizamos o resultado computado ou a mensagem de erro em dois caminhos de código separados.

Promise.allSettled

Promise.allSettled te dá um sinal quando todas as promessas de entrada são decididas, o que significa que elas são resolvidas ou rejeitadas. Isso é útil nos casos em que você não se importa com o estado das promessas, você só quer saber quando o trabalho está concluído, independentemente de ter sido bem-sucedido.

Por exemplo, você pode dar início a uma série de chamadas de API independentes e usa Promise.allSettled para garantir que todas sejam concluídas antes de fazer outra coisa, como remover um controle giratório de carregamento:

const promises = [
fetch('/api-call-1'),
fetch('/api-call-2'),
fetch('/api-call-3'),
];
// Imagine que algumas dessas requisições falhem e outras são bem-sucedidas

await Promise.allSettled(promises);

// Todas as chamadas de API foram finalizadas (falhas e sucessos)
removeLoadingIndicator();

Promise.any

Promise.any te dá um sinal assim que uma das promessas se cumprem. Isso é semelhante a Promise.race, exceto que any não rejeita cedo quando uma das promessas é rejeitada.

const promises = [
fetch('/endpoint-a').then(() => 'a'),
fetch('/endpoint-b').then(() => 'b'),
fetch('/endpoint-c').then(() => 'c'),
];
try {
const first = await Promise.any(promises);
// Qualquer uma das promessas foi cumprida.
// → e.g. 'b'
console.log(first);
} catch (error) {
// Todas as promessas foram rejeitadas.
console.log(error);
}

Este exemplo de código verifica qual endpoint responde o mais rápido e faz o log dele. Somente se todos os pedidos falharem, acabaremos no bloco catch, onde poderemos lidar com os erros.

Créditos ⭐️

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store