Promise

O objeto Promise representa a eventual conclusão (ou falha) de uma operação assíncrona e seu valor resultante.

Para saber como as promises funcionam e como você pode usá-las, recomendamos que você leia Usando promises primeiro.

Descrição

Uma Promise é um proxy para um valor não necessariamente conhecido quando a promise é criada. Ele permite que você associe manipuladores ao valor de sucesso ou motivo de falha de uma ação assíncrona. Isso permite que métodos assíncronos retornem valores como métodos síncronos: em vez de retornar imediatamente o valor final, o método assíncrono retorna uma promise para fornecer o valor em algum momento no futuro.

Uma Promise está em um destes estados:

  • pending: estado inicial, nem cumprido nem rejeitado.
  • fulfilled: significa que a operação foi concluída com sucesso.
  • rejected: significa que a operação falhou.

O estado eventual de uma promise pendente pode ser fulfilled com um valor ou rejected com um motivo (erro). Quando uma dessas opções ocorre, os manipuladores associados enfileirados pelo método then de uma promise são chamados. Se a promise já tiver sido cumprida ou rejeitada quando um manipulador correspondente for anexado, o manipulador será chamado, portanto, não há condição de corrida entre a conclusão de uma operação assíncrona e a anexação de seus manipuladores.

Uma promise é considerada resolvida se for cumprida ou rejeitada, mas não pendente.

Fluxograma mostrando como o estado Promise transita entre pendente, cumprido e rejeitado por meio de manipuladores then/catch. Uma promise pendente pode ser cumprida ou rejeitada. Se preenchido, o manipulador "on fulfillment", ou primeiro parâmetro do método then(), é executado e realiza outras ações assíncronas. Se rejeitado, o manipulador de erro, passado como o segundo parâmetro do método then() ou como o único parâmetro do método catch(), é executado.

Você também ouvirá o termo resolved usado com promises — isso significa que a promise é liquidada ou "bloqueada" para corresponder ao estado eventual de outra promise e resolvê-la ou rejeitá-la não tem efeito. O documento Estados e destinos da proposta original da Promise contém mais detalhes sobre a terminologia da promise. Coloquialmente, promises "resolvidas" geralmente são equivalentes a promises "cumpridas", mas, conforme ilustrado em "Estados e destinos", as promises resolvidas também podem estar pendentes ou rejeitadas. Por exemplo:

js
new Promise((resolveOuter) => {
  resolveOuter(
    new Promise((resolveInner) => {
      setTimeout(resolveInner, 1000);
    }),
  );
});

Essa promise já está resolvida no momento em que é criada (porque o resolveOuter é chamado de forma síncrona), mas é resolvido com outra promise e, portanto, não será cumprida até 1 segundo depois, quando a promise interna for cumprida. Na prática, a "resolução" muitas vezes é feita nos bastidores e não observável, e apenas o seu cumprimento ou rejeição o são.

Nota: várias outras linguagens têm mecanismos para avaliação preguiçosa e adiamento de uma computação, que eles também chamam de "promises", por exemplo, Esquema. As promises em JavaScript representam processos que já estão acontecendo, que podem ser encadeados com funções de retorno de chamada. Se você deseja avaliar lentamente uma expressão, considere usar uma função sem argumentos, por exemplo. f = () => expressão para criar a expressão avaliada lentamente e f() para avaliar a expressão imediatamente.

Promises em cadeia

Os métodos Promise.prototype.then(), Promise.prototype.catch() e Promise.prototype.finally() são usados para associar uma ação adicional com uma promise que se torna liquidada. Como Promise.prototype.then() e Promise.prototype.catch() métodos retornam promises, eles podem ser encadeados.

O método .then() aceita até dois argumentos; o primeiro argumento é uma função de retorno de chamada para o caso cumprido da promise e o segundo argumento é uma função de retorno de chamada para o caso rejeitado. Cada .then() retorna um objeto de promise recém-gerado, que pode ser usado opcionalmente para encadeamento; por exemplo:

js
const minhaPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("foo");
  }, 300);
});

minhaPromise
  .then(handleFulfilledA, handleRejectedA)
  .then(handleFulfilledB, handleRejectedB)
  .then(handleFulfilledC, handleRejectedC);

O processamento continua para o próximo elo da cadeia mesmo quando um .then() não possui uma função de retorno de chamada que retorna um objeto Promise. Portanto, uma cadeia pode omitir com segurança cada rejection função de retorno de chamada até o .catch() final.

Manipular uma promise rejeitada em cada .then() tem consequências mais adiante na cadeia de promises. Às vezes não há escolha, porque um erro deve ser tratado imediatamente. Nesses casos, devemos lançar um erro de algum tipo para manter o estado de erro na cadeia. Por outro lado, na ausência de uma necessidade imediata, é mais simples deixar de fora o tratamento de erros até uma instrução .catch() final. Um .catch() é realmente apenas um .then() sem um slot para uma função de retorno de chamada para o caso em que a promise é cumprida.

js
minhaPromise
  .then(handleFulfilledA)
  .then(handleFulfilledB)
  .then(handleFulfilledC)
  .catch(handleRejectedAny);

Usando Arrow Function Expressions para as funções de retorno de chamada, a implementação da cadeia de promises pode ser algo assim:

js
minhaPromise
  .then((valor) => `${valor} e barra`)
  .then((valor) => `${valor} e barra novamente`)
  .then((valor) => `${valor} e novamente`)
  .then((valor) => `${valor} e novamente`)
  .then((valor) => {
    console.log(valor);
  })
  .catch((erro) => {
    console.error(err);
  });

Nota: para uma execução mais rápida, todas as ações síncronas devem ser feitas preferencialmente em um manipulador, caso contrário, seriam necessários vários tiques para executar todos os manipuladores em sequência.

A condição de término de uma promise determina o estado "estabelecido" da próxima promise na cadeia. Um estado "cumprido" indica uma conclusão bem-sucedida da promise, enquanto um estado "rejeitado" indica falta de sucesso. O valor de retorno de cada promise cumprida na cadeia é passado para o próximo .then(), enquanto o motivo da rejeição é passado para a próxima função de tratamento de rejeição na cadeia.

As promises de uma corrente são aninhadas como bonecas russas, mas são estouradas como o topo de uma pilha. A primeira promise na cadeia está mais profundamente aninhada e é a primeira a aparecer.

(promise D, (promise C, (promise B, (promise A))))

Quando um nextValue é uma promise, o efeito é uma substituição dinâmica. O return faz com que uma promise seja exibida, mas a promise nextValue é colocada em seu lugar. Para o aninhamento mostrado acima, suponha que .then() associado a "promise B" retorne um nextValue de "promise X". O aninhamento resultante ficaria assim:

(promise D, (promise C, (promise X)))

Uma promise pode participar de mais de um aninhamento. Para o código a seguir, a transição de promiseA para um estado "settled" fará com que ambas as instâncias de .then() sejam invocadas.

js
const promiseA = new Promise(myExecutorFunc);
const promiseB = promiseA.then(handleFulfilled1, handleRejected1);
const promiseC = promiseA.then(handleFulfilled2, handleRejected2);

Uma ação pode ser atribuída a uma promise já "estabelecida". Nesse caso, a ação (se apropriado) será executada na primeira oportunidade assíncrona. Observe que as promises são garantidas como assíncronas. Portanto, uma ação para uma promise já "estabelecida" ocorrerá somente depois que a pilha for limpa e um tique-taque do relógio passar. O efeito é muito parecido com setTimeout(action,10).

js
const promiseA = new Promise((resoluçãoFunc, rejeiçãoFunc) => {
  resoluçãoFunc(777);
});
// Neste ponto, "promiseA" já está liquidado.
promiseA.then((val) => console.log("log assíncrono tem val:", val));
console.log("log imediato");

// produz saída nesta ordem:
// registro imediato
// registro assíncrono tem valor: 777

Thenables

O ecossistema JavaScript fez várias implementações do Promise muito antes de se tornar parte da linguagem. Apesar de serem representados de forma diferente internamente, no mínimo, todos os objetos do tipo Promise implementam a interface Thenable. Um thenable implementa o método .then(), que é chamado com dois retornos de chamada: um para quando a promise é cumprida, um para quando for rejeitado. Promises também são possíveis.

Para interoperar com as implementações de Promise existentes, a linguagem permite o uso de thenables no lugar de promises. Por exemplo, Promise.resolve não apenas resolverá as promises, mas também rastreará os entãoables.

js
const aThenable = {
  then(onFulfilled, onRejected) {
    onCumprido({
      // O thenable é preenchido com outro thenable
      then(onFulfilled, onRejected) {
        onCumprido(42);
      },
    });
  },
};

Promise.resolve(aThenable); // Uma promise cumprida com 42

Rastreamento de objetos de configurações incumbentes

Um objeto de configurações é um ambiente que fornece informações adicionais quando o código JavaScript está em execução. Isso inclui o mapa do domínio e do módulo, bem como informações específicas do HTML, como a origem. O objeto de configurações incumbente é rastreado para garantir que o navegador saibas qual usar para um determinado pedaço de código de usuário.

Para melhor visualizar isso, podemos dar uma olhada mais de perto em como o reino pode ser um problema. Um reino pode ser pensado aproximadamente como o objeto global. O que é único sobre os realms é que eles contêm todas as informações necessárias para executar o código JavaScript. Isso inclui objetos como Array e Error. Cada objeto de configurações tem sua própria "cópia" e não são compartilhados. Isso pode causar algum comportamento inesperado em relação às promises. Para contornar isso, rastreamos algo chamado objeto de configurações incumbentes. Isso representa informações específicas do contexto do código do usuário responsável por uma determinada chamada de função.

Para ilustrar isso um pouco mais, podemos dar uma olhada em como um <iframe> embutido em um documento se comunica com seu host. Como todas as APIs da Web estão cientes do objeto de configurações incumbente, o seguinte funcionará em todos os navegadores:

html
<!doctype html> <iframe></iframe>
<!-- temos um reino aqui -->
<script>
  // temos um reino aqui também
  const bound = frames[0].postMessage.bind(frames[0], "alguns dados", "*");
  // bound é uma função embutida — não há usuário
  // código na pilha, então qual reino usamos?
  setTimeout(limite);
  // isso ainda funciona, porque usamos o mais novo
  // reino (o titular) na pilha
</script>

O mesmo conceito se aplica às promises. Se modificarmos um pouco o exemplo acima, obtemos isso:

html
<!doctype html> <iframe></iframe>
<!-- temos um reino aqui -->
<script>
  // temos um reino aqui também
  const bound = frames[0].postMessage.bind(frames[0], "alguns dados", "*");
  // bound é uma função incorporada — não há usuário
  // código na pilha — qual reino usamos?
  Promise.resolve(undefined).then(bound);
  // isso ainda funciona, porque usamos o mais novo
  // reino (o titular) na pilha
</script>

Se alterarmos isso para que o <iframe> no documento esteja ouvindo as mensagens de postagem, podemos observar o efeito do objeto de configurações incumbente:

html
<!-- y.html -->
<!doctype html>
<iframe src="x.html"></iframe>
<script>
  const bound = frames[0].postMessage.bind(frames[0], "alguns dados", "*");
  Promise.resolve(undefined).then(bound);
</script>
html
<!-- x.html -->
<!doctype html>
<script>
  window.addEventListener(
    "mensagem",
    (evento) => {
      document.querySelector("#texto").textContent = "olá";
      // este código será executado apenas em navegadores que rastreiam o objeto de configurações incumbente
      console.log(evento);
    },
    falso,
  );
</script>

No exemplo acima, o texto interno do <iframe> será atualizado somente se o objeto de configurações incumbente for rastreado. Isso porque, sem rastrear o titular, podemos acabar usando o ambiente errado para enviar a mensagem.

Nota: atualmente, o rastreamento de realm incumbente é totalmente implementado no Firefox e tem implementações parciais no Chrome e no Safari.

Construtor

Promise() (en-US)

Cria um novo objeto Promise. O construtor é usado principalmente para encapsular funções que ainda não suportam promises.

Métodos estáticos

Promise.all(iterable)

Aguarde que todas as promises sejam cumpridas ou que alguma seja rejeitada.

Se a promise retornada for cumprida, ela será cumprida com uma matriz de agregação dos valores das promises cumpridas, na mesma ordem definida no iterável de várias promises.

Se for rejeitado, será rejeitado com o motivo da primeira promise no iterável que foi rejeitada.

Promise.allSettled(iterable)

Aguarde até que todas as promises sejam resolvidas (cada uma pode cumprir ou rejeitar).

Retorna uma Promise que cumpre depois que todas as promises fornecidas são cumpridas ou rejeitadas, com uma matriz de objetos que descrevem o resultado de cada promise.

Promise.any(iterable) (en-US)

Pega um iterável de objetos Promise e, assim que uma das promises do iterável é cumprida, retorna uma única promise que cumpre com o valor dessa promise.

Promise.race(iterable)

Aguarde até que alguma das promises seja cumprida ou rejeitada.

Se a promise retornada for cumprida, ela será cumprida com o valor da primeira promise no iterável que foi cumprida.

Se for rejeitado, será rejeitado com o motivo da primeira promise que foi rejeitada.

Promise.reject(reason)

Retorna um novo objeto Promise que é rejeitado com o motivo fornecido.

Promise.resolve(value)

Retorna um novo objeto Promise que é resolvido com o valor fornecido. Se o valor for um thenable (ou seja, tem um método then), a promise retornada "seguirá" esse thenable, adotando seu estado eventual; caso contrário, a promise devolvida será cumprida com o valor.

Geralmente, se você não sabe se um valor é uma promise ou não, Promise.resolve(value) e trabalhe com o valor de retorno como uma promise.

Métodos de instância

Consulte o Guia do Microtask (en-US) para saber mais sobre como esses métodos usam a fila e os serviços do Microtask.

Promise.prototype.catch()

Anexa um retorno de chamada do manipulador de rejeição à promise e retorna uma nova promise resolvendo o valor de retorno do retorno de chamada se for chamado ou seu valor de cumprimento original se a promise for cumprida.

Promise.prototype.then()

Anexa manipuladores de cumprimento e rejeição à promise e retorna uma nova promise resolvendo o valor de retorno do manipulador chamado ou seu valor estabelecido original se a promise não foi tratada (ou seja, se o manipulador relevante onFulfilled ou onRejected não é uma função).

Promise.prototype.finally()

Anexa um manipulador à promise e retorna uma nova promise que é resolvida quando a promise original é resolvida. O manipulador é chamado quando a promise é liquidada, seja cumprida ou rejeitada.

Exemplos

Exemplo básico

js
const minhaPrimeiraPromise = new Promise((resolve, reject) => {
  // Chamamos resolve(...) quando o que estávamos fazendo de forma assíncrona foi bem-sucedido e reject(...) quando falhou.
  // Neste exemplo, usamos setTimeout(...) para simular código assíncrono.
  // Na realidade, você provavelmente estará usando algo como XHR ou uma API HTML.
  setTimeout(() => {
    resolve("Sucesso!"); // Yay! Tudo ocorreu bem!
  }, 250);
});

minhaPrimeiraPromise.then((mensagem de sucesso) => {
  // successMessage é o que passamos na função resolve(...) acima.
  // Não precisa ser uma string, mas se for apenas uma mensagem de sucesso, provavelmente será.
  console.log(`Yay! ${successMessage}`);
});

Exemplo com diversas situações

Este exemplo mostra diversas técnicas para usar os recursos do Promise e diversas situações que podem ocorrer. Para entender isso, comece rolando até a parte inferior do bloco de código e examine a cadeia de promises. Após o fornecimento de uma promise inicial, uma cadeia de promises pode seguir. A cadeia é composta de chamadas .then(), e normalmente (mas não necessariamente) tem um único .catch() no final, opcionalmente seguido por .finally(). Neste exemplo, a cadeia de promises é iniciada por uma construção new Promise() escrita sob medida; mas, na prática, as cadeias de promises geralmente começam com uma função de API (escrita por outra pessoa) que retorna uma promise.

A função de exemplo tetheredGetNumber() mostra que um gerador de promise utilizará reject() ao configurar uma chamada assíncrona, ou dentro do call-back, ou ambos. A função promiseGetWord() ilustra como uma função de API pode gerar e retornar uma promise de maneira independente.

Observe que a função troubleWithGetNumber() termina com um throw. Isso é forçado porque uma cadeia de promises passa por todas as promises .then(), mesmo após um erro, e sem o throw, o erro pareceria "corrigido". Isso é um aborrecimento, e por esta razão, é comum omitir rejectionFunc em toda a cadeia de promises .then(), e ter apenas um único rejectionFunc no catch() final.

Este código pode ser executado em NodeJS. A compreensão é aprimorada ao ver os erros realmente ocorrerem. Para forçar mais erros, altere os valores de threshold.

js
// Para experimentar o tratamento de erros, os valores "threshold" causam erros aleatoriamente
const THRESHOLD_A = 8; // pode usar zero 0 para garantir o erro

function tetheredGetNumber(resolve, reject) {
  setTimeout(() => {
    const randomInt = Date.now();
    const value = randomInt % 10;
    if (value < THRESHOLD_A) {
      resolve(value);
    } else {
      reject(`Muito grande: ${value}`);
    }
  }, 500);
}

function determinaParidade(valor) {
  const isOdd = valor % 2 === 1;
  return { valor, isOdd };
}

function troubleWithGetNumber(reason) {
  const err = new Error("Problema para obter o número", { cause: reason });
  console.error(err);
  throw err;
}

function promiseGetWord(parityInfo) {
  return new Promise((resolve, reject) => {
    const { value, isOdd } = parityInfo;
    if (value >= THRESHOLD_A - 1) {
      reject(`Ainda muito grande: ${valor}`);
    } else {
      parityInfo.wordEvenOdd = isOdd ? "ímpar" : "par";
      resolve(parityInfo);
    }
  });
}

new Promise(tetheredGetNumber)
  .then(determineParity, troubleWithGetNumber)
  .then(promiseGetWord)
  .then((info) => {
    console.log(`Recebido: ${info.value}, ${info.wordEvenOdd}`);
    return info;
  })
  .catch((reason) => {
    if (reason.cause) {
      console.error("Já havia tratado o erro anteriormente");
    } else {
      console.error(`Problema com promiseGetWord(): ${reason}`);
    }
  })
  .finally((info) => console.log("Tudo pronto"));

Exemplo Avançado

Este pequeno exemplo mostra o mecanismo of a Promise. O método testPromise() é chamado cada vez que o <button> é clicado. Ele cria uma promise que será cumprida, usando setTimeout() (en-US), para a contagem de promises (número começando em 1) a cada 1-3 segundos, aleatoriamente. O construtor Promise() é usado para criar a promise.

O cumprimento da promise é registrado, por meio de um callback de cumprimento definido usando p1.then(). Alguns logs mostram como a parte síncrona do método é dissociada da conclusão assíncrona da promise.

Ao clicar no botão várias vezes em um curto espaço de tempo, você verá até as diferentes promises sendo cumpridas uma após a outra.

HTML

html
<button id="make-promise">Faça uma promise!</button>
<div id="log"></div>

JavaScript

js
"use strict";

let promiseCount = 0;

function testPromise() {
  const thisPromiseCount = ++promiseCount;
  const log = document.getElementById("log");
  // começar
  log.insertAdjacentHTML("beforeend", `${thisPromiseCount}) Iniciado<br>`);
  // Fazemos uma nova promise: prometemos uma contagem numérica dessa promise,
  // a partir de 1 (após esperar 3s)
  const p1 = new Promise((resolve, reject) => {
    // A função executora é chamada com a habilidade
    // para resolver ou rejeitar a promise
    log.insertAdjacentHTML(
      "beforeend",
      `${thisPromiseCount}) Construtor de promise<br>`,
    );
    // Este é apenas um exemplo para criar assincronismo
    setTimeout(
      () => {
        // Nós cumprimos a promise
        resolve(thisPromiseCount);
      },
      Math.random() * 2000 + 1000,
    );
  });

  // Definimos o que fazer quando a promise é resolvida com a chamada then(),
  // e o que fazer quando a promise é rejeitada com a chamada catch()
  p1.then((val) => {
    // Registra o valor de atendimento
    log.insertAdjacentHTML("beforeend", `${val}) Promise cumprida<br>`);
  }).catch((reason) => {
    // Registra o motivo da rejeição
    console.log(`Manuseie a promise rejeitada (${reason}) aqui.`);
  });
  // fim
  log.insertAdjacentHTML("beforeend", `${thisPromiseCount}) Promise feita<br>`);
}

const btn = document.getElementById("make-promise");
btn.addEventListener("click", testPromise);

Resultado

Carregando uma imagem com XHR

Outro exemplo simples usando Promise e XMLHttpRequest para carregar uma imagem está disponível no MDN GitHub js-examples. Você também pode ver em ação. Cada etapa é comentada e permite acompanhar de perto a arquitetura Promise e XHR.

Especificações

Specification
ECMAScript Language Specification
# sec-promise-objects

Compatibilidade do navegador

BCD tables only load in the browser

Veja também