Motivação

Até agora consideramos que as funções assíncronas sempre serão bem-sucedidas em suas respectivas tarefas. Na prática, erros podem acontecer. Por exemplo, uma função que faz o download de um arquivo pode falhar porque o servidor está fora do ar ou muito lento; nesse caso, a função deve indicar o erro ao código que a chamou.

Um padrão comum para possibilitar o tratamento de erros em funções assíncronas é usar dois callbacks: um que será chamado em caso de sucesso, e o outro que será chamado em caso de erro.

No exemplo abaixo, a função assíncrona sorteia0 retorna um número aleatório entre 0 e maximo, mas tem 20% de chance de falhar. Ela recebe dois callbacks: a função resolve, que será chamada em caso de sucesso, e a função reject, que será chamada em caso de falha.

Se quisermos agora encadear uma sequência de sorteios, de tal forma que um sorteio só inicie quando o sorteio anterior for concluído com sucesso, o código tende a ficar difícil de ler e entender.

Promises

Promises são objetos (da classe Promise) que encapsulam uma chamada a uma função assíncrona que possui callbacks de sucesso e erro.

(Um conceito relacionado é o de future, presente em outras linguagens de programação.)

Uma promise pode estar em um dos seguintes estados:

  • pendente: a operação ainda não foi finalizada
  • resolvida: a operação foi finalizada com sucesso
  • rejeitada: a operação foi finalizada com erro

Uma promise que se encontra no estado resolvida ou rejeitada (isto é, não está pendente) é dita estabelecida.

Podemos reescrever a função assíncrona sorteia de forma que ela retorne uma promise. O código que usa a função deve então chamar os métodos then() e catch() da classe Promise para tratar o resultado:

  • then(f) recebe uma função, f que deve ser executada quando a promise é resolvida
  • catch(f) recebe uma função, f que deve ser executada quando a promise é rejeitada
  • Tanto then(f) quanto catch(f) retornam uma promise que será resolvida com o valor retornado por f.

Execute o código a seguir para carregar a função sorteia, que será usada nos outros exemplos.

Execução sequencial de promises

Como o then() retorna uma promise, você pode encadear chamadas a then()/catch(), que serão executadas de forma sequencial:

Execução paralela de promises: Promise.all

Promise.all recebe um array de promises e cria um novo promise que termina somente quando todos os promises da array terminam. Em outras palavras, Promise.all permite executar várias operações assíncronas em paralelo e executar alguma operação (usando o then()) quando todas elas forem resolvidas. A função passada ao then() recebe os valores de todos os promises em um array. Se alguma operação for rejeitada, a promise resultante também será rejeitada, imediatamente.

Execução paralela de promises: Promise.race

Assim como Promise.all, Promise.race também cria uma nova promise a partir de um array de promises, e permite executar as promises do array em paralelo. A diferença é que a nova promise é estabelecida (resolvida ou rejeitada) logo que qualquer promise do array for estabelecida.

Referências