[ Web Proxy ]
URL:
Viewing: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Using_promises [Back]  [Original]

Usando promises - JavaScript | MDN

Esta pgina foi traduzida do ingls pela comunidade. Saiba mais e junte-se comunidade MDN Web Docs.

View in English Always switch to English

Usando promises

Uma Promise um objeto que representa a eventual concluso ou falha de uma operao assncrona. Como a maioria das pessoas consomem promises j criadas, este guia explicar o consumo de promises devolvidas antes de explicar como cri-las.

Essencialmente, uma promise um objeto retornado para o qual voc adiciona callbacks, em vez de passar callbacks para uma funo.

Por exemplo, em vez de uma funo old-style que espera dois callbacks, e chama um deles em uma eventual concluso ou falha:

js
function successCallback(result) {
  console.log("It succeeded with " + result);
}

function failureCallback(error) {
  console.log("It failed with " + error);
}

doSomething(successCallback, failureCallback);

funes modernas retornam uma promise e ento voc pode adicionar seus callbacks:

js
const promise = doSomething();
promise.then(successCallback, failureCallback);

ou simplesmente:

js
doSomething().then(successCallback, failureCallback);

Ns chamamos isso de chamada de funo assncrona. Essa conveno tem vrias vantagens. Vamos explorar cada uma delas.

In this article

Garantias

Ao contrrio dos callbacks com retornos de funes old-style, uma promise vem com algumas garantias:

  • Callbacks nunca sero chamados antes da concluso da execuo atual do loop de eventos do JavaScript.
  • Callbacks adicionadas com .then mesmo depois do sucesso ou falha da operao assncrona, sero chamadas, como acima.
  • Multiplos callbacks podem ser adicionados chamando-se .then vrias vezes, para serem executados independentemente da ordem de insero.

Mas o benefcio mais imediato das promises o encadeamento.

Encadeamento

Uma necessidade comum executar duas ou mais operaes assncronas consecutivas, onde cada operao subsequente comea quando a operao anterior bem sucedida, com o resultado do passo anterior. Ns conseguimos isso criando uma cadeia de promises.

Aqui est a mgica: a funo then retorna uma nova promise, diferente da original:

js
const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);

ou

js
const promise2 = doSomething().then(successCallback, failureCallback);

Essa segunda promise representa a concluso no apenas de doSomething(), mas tambm do successCallback ou failureCallback que voc passou, que podem ser outras funes assncronas que retornam uma promise. Quando esse for o caso, quaisquer callbacks adicionados a promise2 sero enfileiradas atrs da promise retornada por successCallback ou failureCallback.

Basicamente, cada promise representa a completude de outro passo assncrono na cadeia.

Antigamente, realizar operaes assncronas comuns em uma linha levaria clssica pirmide da desgraa:

js
doSomething(function (result) {
  doSomethingElse(
    result,
    function (newResult) {
      doThirdThing(
        newResult,
        function (finalResult) {
          console.log("Got the final result: " + finalResult);
        },
        failureCallback,
      );
    },
    failureCallback,
  );
}, failureCallback);

Ao invs disso, com funes modernas, ns atribumos nossas callbacks s promises retornadas, formando uma cadeia de promise:

js
doSomething()
  .then(function (result) {
    return doSomethingElse(result);
  })
  .then(function (newResult) {
    return doThirdThing(newResult);
  })
  .then(function (finalResult) {
    console.log("Got the final result: " + finalResult);
  })
  .catch(failureCallback);

Os argumentos para then so opcionais, e catch(failureCallback) uma abreviao para then(null, failureCallback). Voc pode tambm pode ver isso escrito com arrow functions:

js
doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);

Importante: Sempre retorne um resultado, de outra forma as callbacks no vo capturar o resultado da promise anterior.

Encadeando depois de um catch

possivel encadear depois de uma falha, i.e um catch. Isso muito til para realizar novas aes mesmo depois de uma falha no encadeamento. Leia o seguinte exemplo:

js
new Promise((resolve, reject) => {
  console.log("Initial");

  resolve();
})
  .then(() => {
    throw new Error("Something failed");

    console.log("Do this");
  })
  .catch(() => {
    console.log("Do that");
  })
  .then(() => {
    console.log("Do this whatever happened before");
  });

Isso vai produzir o seguinte texto:

Initial
Do that
Do this whatever happened before

Observe que o texto "Do this" no foi impresso por conta que o erro "Something failed" causou uma rejeio.

Propagao de erros

Na pirmide da desgraa vista anteriormente, voc pode se lembrar de ter visto failureCallback trs vezes, em comparao a uma nica vez no fim da corrente de promises:

js
doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => console.log(`Got the final result: ${finalResult}`))
  .catch(failureCallback);

Basicamente, uma corrente de promises para se houver uma exceo, procurando por catch handlers no lugar. Essa modelagem de cdigo segue bastante a maneira de como o cdigo sncrono funciona:

js
try {
  const result = syncDoSomething();
  const newResult = syncDoSomethingElse(result);
  const finalResult = syncDoThirdThing(newResult);
  console.log(`Got the final result: ${finalResult}`);
} catch (error) {
  failureCallback(error);
}

Essa simetria com cdigo assncrono resulta no syntactic sugar async/await presente no ECMAScript 2017:

js
async function foo() {
  try {
    const result = await doSomething();
    const newResult = await doSomethingElse(result);
    const finalResult = await doThirdThing(newResult);
    console.log(`Got the final result: ${finalResult}`);
  } catch (error) {
    failureCallback(error);
  }
}

construdo sobre promises, por exemplo, doSomething() a mesma funo que antes. Leia mais sobre a sintaxe aqui.

Por pegar todos os erros, at mesmo excees jogadas(thrown exceptions) e erros de programao, as promises acabam por solucionar uma falha fundamental presente na pirmide da desgraa dos callbacks. Essa caracterstica essencial para a composio funcional das operaes assncronas.

Criando uma Promise em torno de uma callback API antiga

Uma Promise pode ser criada do zero utilizando o seu construtor. Isto deve ser necessrio apenas para o envolvimento de APIs antigas.

Em um mundo ideal, todas as funes assncronas j retornariam promises. Infelizmente, algumas APIs ainda esperam que os retornos de sucesso e/ou falha sejam passados da maneira antiga. O exemplo por excelncia o setTimeout() function:

js
setTimeout(() => saySomething("10 seconds passed"), 10000);

Misturar chamadas de retorno e promises de old-style problemtico. Se saySomething falhar ou contiver um erro de programao, nada o captura.

Por sorte ns podemos envolv-la em uma promise. uma boa prtica envolver funes problemticas no menor nivel possvel, e nunca cham-las diretamente de novo:

js
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

wait(10000)
  .then(() => saySomething("10 seconds"))
  .catch(failureCallback);

Basicamente, um construtor de promises pega uma funo executora que nos deixa resolver ou rejeitar uma promise manualmente. Desde que setTimeout no falhe, ns deixamos a rejeio de fora neste caso.

Composio

Promise.resolve() e Promise.reject() so atalhos para se criar manualmente uma promise que j foi resolvida ou rejeitada, respectivamente. Isso pode ser til em algumas situaes.

Promise.all() e Promise.race() so duas ferramentas de composio para se executar operaes assncronas em paralelo.

Uma composio sequencial possvel usando JavaScript de uma forma esperta:

js
[func1, func2].reduce((p, f) => p.then(f), Promise.resolve());

Basicamente reduzimos um vetor de funes assncronas a uma cadeia de promises equivalentes a: Promise.resolve().then(func1).then(func2);

Isso tambm pode ser feito com uma funo de composio reutilizvel, que comum em programao funcional:

js
const applyAsync = (acc, val) => acc.then(val);
const composeAsync =
  (...funcs) =>
  (x) =>
    funcs.reduce(applyAsync, Promise.resolve(x));

A funo composeAsync aceitar qualquer nmero de funes como argumentos e retornar uma nova funo que aceita um valor inicial a ser passado pelo pipeline de composio. Isso benfico porque alguma, ou todas as funes, podem ser assncronas ou sncronas, e garantido de que sero executadas na ordem correta.

js
const transformData = composeAsync(func1, asyncFunc1, asyncFunc2, func2);
transformData(data);

No ECMAScript 2017, uma composio sequencial pode ser feita de forma mais simples com async/await:

js
for (const f of [func1, func2]) {
  await f();
}

Cronometragem

Para evitar surpresas, funes passadas para then nunca sero chamadas sincronamente, mesmo com uma funo j resolvida:

js
Promise.resolve().then(() => console.log(2));
console.log(1); // 1, 2

Ao invs de rodar imediatamente, a funo passada colocada em uma micro tarefa, o que significa que ela roda depois que a fila estiver vazia no final do atual processo de evento de loop do Javascript, ou seja: muito em breve:

js
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

wait().then(() => console.log(4));
Promise.resolve()
  .then(() => console.log(2))
  .then(() => console.log(3));
console.log(1); // 1, 2, 3, 4

Ver tambm


Web Proxy Viewer  |  New URL  |  Original Page