From d53bae971e455859229bcb3246e29bcbc85179d2 Mon Sep 17 00:00:00 2001 From: MDN Date: Tue, 8 Mar 2022 00:12:08 +0000 Subject: [CRON] sync translated content --- files/pt-br/_redirects.txt | 8 +- files/pt-br/_wikihistory.json | 40 +- .../learn/javascript/asynchronous/index.html | 524 +++++++++++++++++ .../javascript/asynchronous/introducing/index.html | 156 +++++ .../javascript/asynchronous/promises/index.html | 556 ++++++++++++++++++ .../index.html | 625 +++++++++++++++++++++ .../javascript/asynchronous/async_await/index.html | 556 ------------------ .../choosing_the_right_approach/index.html | 524 ----------------- .../javascript/asynchronous/concepts/index.html | 156 ----- .../asynchronous/timeouts_and_intervals/index.html | 624 -------------------- 10 files changed, 1887 insertions(+), 1882 deletions(-) create mode 100644 files/pt-br/conflicting/learn/javascript/asynchronous/index.html create mode 100644 files/pt-br/conflicting/learn/javascript/asynchronous/introducing/index.html create mode 100644 files/pt-br/conflicting/learn/javascript/asynchronous/promises/index.html create mode 100644 files/pt-br/conflicting/learn/javascript/asynchronous_ae5a561b0ec11fc53c167201aa8af5df/index.html delete mode 100644 files/pt-br/learn/javascript/asynchronous/async_await/index.html delete mode 100644 files/pt-br/learn/javascript/asynchronous/choosing_the_right_approach/index.html delete mode 100644 files/pt-br/learn/javascript/asynchronous/concepts/index.html delete mode 100644 files/pt-br/learn/javascript/asynchronous/timeouts_and_intervals/index.html (limited to 'files/pt-br') diff --git a/files/pt-br/_redirects.txt b/files/pt-br/_redirects.txt index b1d7aa2343..3363b351ae 100644 --- a/files/pt-br/_redirects.txt +++ b/files/pt-br/_redirects.txt @@ -391,9 +391,13 @@ /pt-BR/docs/Learn/Common_questions/Que_software_eu_preciso /pt-BR/docs/Learn/Common_questions/What_software_do_I_need /pt-BR/docs/Learn/Common_questions/ferramentas_de_desenvolvimento_do_navegador /pt-BR/docs/Learn/Common_questions/What_are_browser_developer_tools /pt-BR/docs/Learn/Common_questions/o_que_e_um_web_server /pt-BR/docs/Learn/Common_questions/What_is_a_web_server -/pt-BR/docs/Learn/JavaScript/Asynchronous/Conceitos /pt-BR/docs/Learn/JavaScript/Asynchronous/Concepts -/pt-BR/docs/Learn/JavaScript/Asynchronous/Escolhendo_abordagem_correta /pt-BR/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach +/pt-BR/docs/Learn/JavaScript/Asynchronous/Async_await /pt-BR/docs/conflicting/Learn/JavaScript/Asynchronous/Promises +/pt-BR/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach /pt-BR/docs/conflicting/Learn/JavaScript/Asynchronous +/pt-BR/docs/Learn/JavaScript/Asynchronous/Conceitos /pt-BR/docs/conflicting/Learn/JavaScript/Asynchronous/Introducing +/pt-BR/docs/Learn/JavaScript/Asynchronous/Concepts /pt-BR/docs/conflicting/Learn/JavaScript/Asynchronous/Introducing +/pt-BR/docs/Learn/JavaScript/Asynchronous/Escolhendo_abordagem_correta /pt-BR/docs/conflicting/Learn/JavaScript/Asynchronous /pt-BR/docs/Learn/JavaScript/Asynchronous/Introdução /pt-BR/docs/Learn/JavaScript/Asynchronous/Introducing +/pt-BR/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals /pt-BR/docs/conflicting/Learn/JavaScript/Asynchronous_ae5a561b0ec11fc53c167201aa8af5df /pt-BR/docs/Learn/JavaScript/First_steps/Gerador_de_historias_bobas /pt-BR/docs/Learn/JavaScript/First_steps/Silly_story_generator /pt-BR/docs/Learn/JavaScript/First_steps/Matematica /pt-BR/docs/Learn/JavaScript/First_steps/Math /pt-BR/docs/Learn/JavaScript/First_steps/O_que_e_JavaScript /pt-BR/docs/Learn/JavaScript/First_steps/What_is_JavaScript diff --git a/files/pt-br/_wikihistory.json b/files/pt-br/_wikihistory.json index 5b2ed45493..c050bf05f2 100644 --- a/files/pt-br/_wikihistory.json +++ b/files/pt-br/_wikihistory.json @@ -2036,19 +2036,6 @@ "Sheppy" ] }, - "Learn/JavaScript/Asynchronous/Choosing_the_right_approach": { - "modified": "2020-10-15T22:33:23.228Z", - "contributors": [ - "manoelbjr" - ] - }, - "Learn/JavaScript/Asynchronous/Concepts": { - "modified": "2020-10-06T09:57:59.299Z", - "contributors": [ - "joseluizmonteirojr", - "bellammuniz" - ] - }, "Learn/JavaScript/Asynchronous/Introducing": { "modified": "2020-07-16T22:33:17.985Z", "contributors": [ @@ -2062,13 +2049,6 @@ "luan0ap" ] }, - "Learn/JavaScript/Asynchronous/Timeouts_and_intervals": { - "modified": "2020-07-16T22:33:21.635Z", - "contributors": [ - "vbarcellos", - "bellammuniz" - ] - }, "Learn/JavaScript/Building_blocks": { "modified": "2020-07-16T22:31:10.224Z", "contributors": [ @@ -16274,6 +16254,26 @@ "LeonardoPacheco" ] }, + "conflicting/Learn/JavaScript/Asynchronous": { + "modified": "2020-10-15T22:33:23.228Z", + "contributors": [ + "manoelbjr" + ] + }, + "conflicting/Learn/JavaScript/Asynchronous/Introducing": { + "modified": "2020-10-06T09:57:59.299Z", + "contributors": [ + "joseluizmonteirojr", + "bellammuniz" + ] + }, + "conflicting/Learn/JavaScript/Asynchronous_ae5a561b0ec11fc53c167201aa8af5df": { + "modified": "2020-07-16T22:33:21.635Z", + "contributors": [ + "vbarcellos", + "bellammuniz" + ] + }, "conflicting/Learn/JavaScript/Objects": { "modified": "2020-06-07T23:51:22.220Z", "contributors": [ diff --git a/files/pt-br/conflicting/learn/javascript/asynchronous/index.html b/files/pt-br/conflicting/learn/javascript/asynchronous/index.html new file mode 100644 index 0000000000..e1ffe62fbe --- /dev/null +++ b/files/pt-br/conflicting/learn/javascript/asynchronous/index.html @@ -0,0 +1,524 @@ +--- +title: Escolhendo a abordagem correta +slug: conflicting/Learn/JavaScript/Asynchronous +translation_of: Learn/JavaScript/Asynchronous/Choosing_the_right_approach +original_slug: Learn/JavaScript/Asynchronous/Choosing_the_right_approach +--- +
{{LearnSidebar}}
+ +
{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}
+ +

To finish this module off, we'll provide a brief discussion of the different coding techniques and features we've discussed throughout, looking at which one you should use when, with recommendations and reminders of common pitfalls where appropriate. We'll probably add to this resource as time goes on.

+ + + + + + + + + + + + +
Prerequisites:Basic computer literacy, a reasonable understanding of JavaScript fundamentals.
Objective:To be able to make a sound choice of when to use different asynchronous programming techniques.
+ +

Asynchronous callbacks

+ +

Generally found in old-style APIs, involves a function being passed into another function as a parameter, which is then invoked when an asynchronous operation has been completed, so that the callback can in turn do something with the result. This is the precursor to promises; it's not as efficient or flexible. Use only when necessary.

+ + + + + + + + + + + + + + + + + + + +
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoYes (recursive callbacks)Yes (nested callbacks)No
+ +

Code example

+ +

An example that loads a resource via the XMLHttpRequest API (run it live, and see the source):

+ +
function loadAsset(url, type, callback) {
+  let xhr = new XMLHttpRequest();
+  xhr.open('GET', url);
+  xhr.responseType = type;
+
+  xhr.onload = function() {
+    callback(xhr.response);
+  };
+
+  xhr.send();
+}
+
+function displayImage(blob) {
+  let objectURL = URL.createObjectURL(blob);
+
+  let image = document.createElement('img');
+  image.src = objectURL;
+  document.body.appendChild(image);
+}
+
+loadAsset('coffee.jpg', 'blob', displayImage);
+ +

Pitfalls

+ + + +

Browser compatibility

+ +

Really good general support, although the exact support for callbacks in APIs depends on the particular API. Refer to the reference documentation for the API you're using for more specific support info.

+ +

Further information

+ + + +

setTimeout()

+ +

setTimeout() is a method that allows you to run a function after an arbitrary amount of time has passed.

+ + + + + + + + + + + + + + + + + +
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
YesYes (recursive timeouts)Yes (nested timeouts)No
+ +

Code example

+ +

Here the browser will wait two seconds before executing the anonymous function, then will display the alert message (see it running live, and see the source code):

+ +
let myGreeting = setTimeout(function() {
+  alert('Hello, Mr. Universe!');
+}, 2000)
+ +

Pitfalls

+ +

You can use recursive setTimeout() calls to run a function repeatedly in a similar fashion to setInterval(), using code like this:

+ +
let i = 1;
+setTimeout(function run() {
+  console.log(i);
+  i++;
+
+  setTimeout(run, 100);
+}, 100);
+ +

There is a difference between recursive setTimeout() and setInterval():

+ + + +

When your code has the potential to take longer to run than the time interval you’ve assigned, it’s better to use recursive setTimeout() — this will keep the time interval constant between executions regardless of how long the code takes to execute, and you won't get errors.

+ +

Browser compatibility

+ +

{{Compat("api.WindowOrWorkerGlobalScope.setTimeout")}}

+ +

Further information

+ + + +

setInterval()

+ +

setInterval() is a method that allows you to run a function repeatedly with a set interval of time between each execution. Not as efficient as requestAnimationFrame(), but allows you to choose a running rate/frame rate.

+ + + + + + + + + + + + + + + + + +
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoYesNo (unless it is the same one)No
+ +

Code example

+ +

The following function creates a new Date() object, extracts a time string out of it using toLocaleTimeString(), and then displays it in the UI. We then run it once per second using setInterval(), creating the effect of a digital clock that updates once per second (see this live, and also see the source):

+ +
function displayTime() {
+   let date = new Date();
+   let time = date.toLocaleTimeString();
+   document.getElementById('demo').textContent = time;
+}
+
+const createClock = setInterval(displayTime, 1000);
+ +

Pitfalls

+ + + +

Browser compatibility

+ +

{{Compat("api.WindowOrWorkerGlobalScope.setInterval")}}

+ +

Further information

+ + + +

requestAnimationFrame()

+ +

requestAnimationFrame() is a method that allows you to run a function repeatedly, and efficiently, at the best framerate available given the current browser/system. You should, if at all possible, use this instead of setInterval()/recursive setTimeout(), unless you need a specific framerate.

+ + + + + + + + + + + + + + + + + +
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoYesNo (unless it is the same one)No
+ +

Code example

+ +

A simple animated spinner; you can find this example live on GitHub (see the source code also):

+ +
const spinner = document.querySelector('div');
+let rotateCount = 0;
+let startTime = null;
+let rAF;
+
+function draw(timestamp) {
+    if(!startTime) {
+        startTime = timestamp;
+    }
+
+    rotateCount = (timestamp - startTime) / 3;
+
+    if(rotateCount > 359) {
+        rotateCount %= 360;
+    }
+
+    spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
+
+    rAF = requestAnimationFrame(draw);
+}
+
+draw();
+ +

Pitfalls

+ + + +

Browser compatibility

+ +

{{Compat("api.Window.requestAnimationFrame")}}

+ +

Further information

+ + + +

Promises

+ +

Promises are a JavaScript feature that allows you to run asynchronous operations and wait until it is definitely complete before running another operation based on its result. Promises are the backbone of modern asynchronous JavaScript.

+ + + + + + + + + + + + + + + + + +
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoNoYesSee Promise.all(), below
+ +

Code example

+ +

The following code fetches an image from the server and displays it inside an {{htmlelement("img")}} element; see it live also, and see also the source code:

+ +
fetch('coffee.jpg')
+.then(response => response.blob())
+.then(myBlob => {
+  let objectURL = URL.createObjectURL(myBlob);
+  let image = document.createElement('img');
+  image.src = objectURL;
+  document.body.appendChild(image);
+})
+.catch(e => {
+  console.log('There has been a problem with your fetch operation: ' + e.message);
+});
+ +

Pitfalls

+ +

Promise chains can be complex and hard to parse. If you nest a number of promises, you can end up with similar troubles to callback hell. For example:

+ +
remotedb.allDocs({
+  include_docs: true,
+  attachments: true
+}).then(function (result) {
+  let docs = result.rows;
+  docs.forEach(function(element) {
+    localdb.put(element.doc).then(function(response) {
+      alert("Pulled doc with id " + element.doc._id + " and added to local db.");
+    }).catch(function (err) {
+      if (err.name == 'conflict') {
+        localdb.get(element.doc._id).then(function (resp) {
+          localdb.remove(resp._id, resp._rev).then(function (resp) {
+// et cetera...
+ +

It is better to use the chaining power of promises to go with a flatter, easier to parse structure:

+ +
remotedb.allDocs(...).then(function (resultOfAllDocs) {
+  return localdb.put(...);
+}).then(function (resultOfPut) {
+  return localdb.get(...);
+}).then(function (resultOfGet) {
+  return localdb.put(...);
+}).catch(function (err) {
+  console.log(err);
+});
+ +

or even:

+ +
remotedb.allDocs(...)
+.then(resultOfAllDocs => {
+  return localdb.put(...);
+})
+.then(resultOfPut => {
+  return localdb.get(...);
+})
+.then(resultOfGet => {
+  return localdb.put(...);
+})
+.catch(err => console.log(err));
+ +

That covers a lot of the basics. For a much more complete treatment, see the excellent We have a problem with promises, by Nolan Lawson.

+ +

Browser compatibility

+ +

{{Compat("javascript.builtins.Promise")}}

+ +

Further information

+ + + +

Promise.all()

+ +

A JavaScript feature that allows you to wait for multiple promises to complete before then running a further operation based on the results of all the other promises.

+ + + + + + + + + + + + + + + + + +
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoNoNoYes
+ +

Code example

+ +

The following example fetches several resources from the server, and uses Promise.all() to wait for all of them to be available before then displaying all of them — see it live, and see the source code:

+ +
function fetchAndDecode(url, type) {
+  // Returning the top level promise, so the result of the entire chain is returned out of the function
+  return fetch(url).then(response => {
+    // Depending on what type of file is being fetched, use the relevant function to decode its contents
+    if(type === 'blob') {
+      return response.blob();
+    } else if(type === 'text') {
+      return response.text();
+    }
+  })
+  .catch(e => {
+    console.log(`There has been a problem with your fetch operation for resource "${url}": ` + e.message);
+  });
+}
+
+// Call the fetchAndDecode() method to fetch the images and the text, and store their promises in variables
+let coffee = fetchAndDecode('coffee.jpg', 'blob');
+let tea = fetchAndDecode('tea.jpg', 'blob');
+let description = fetchAndDecode('description.txt', 'text');
+
+// Use Promise.all() to run code only when all three function calls have resolved
+Promise.all([coffee, tea, description]).then(values => {
+  console.log(values);
+  // Store each value returned from the promises in separate variables; create object URLs from the blobs
+  let objectURL1 = URL.createObjectURL(values[0]);
+  let objectURL2 = URL.createObjectURL(values[1]);
+  let descText = values[2];
+
+  // Display the images in <img> elements
+  let image1 = document.createElement('img');
+  let image2 = document.createElement('img');
+  image1.src = objectURL1;
+  image2.src = objectURL2;
+  document.body.appendChild(image1);
+  document.body.appendChild(image2);
+
+  // Display the text in a paragraph
+  let para = document.createElement('p');
+  para.textContent = descText;
+  document.body.appendChild(para);
+});
+ +

Pitfalls

+ + + +

Browser compatibility

+ +

{{Compat("javascript.builtins.Promise.all")}}

+ +

Further information

+ + + +

Async/await

+ +

Syntactic sugar built on top of promises that allows you to run asynchronous operations using syntax that's more like writing synchronous callback code.

+ + + + + + + + + + + + + + + + + +
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoNoYesYes (in combination with Promise.all())
+ +

Code example

+ +

The following example is a refactor of the simple promise example we saw earlier that fetches and displays an image, written using async/await (see it live, and see the source code):

+ +
async function myFetch() {
+  let response = await fetch('coffee.jpg');
+  let myBlob = await response.blob();
+
+  let objectURL = URL.createObjectURL(myBlob);
+  let image = document.createElement('img');
+  image.src = objectURL;
+  document.body.appendChild(image);
+}
+
+myFetch();
+ +

Pitfalls

+ + + +

Browser compatibility

+ +

{{Compat("javascript.statements.async_function")}}

+ +

Further information

+ + + +

{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}

+ +

In this module

+ + diff --git a/files/pt-br/conflicting/learn/javascript/asynchronous/introducing/index.html b/files/pt-br/conflicting/learn/javascript/asynchronous/introducing/index.html new file mode 100644 index 0000000000..1d472307c8 --- /dev/null +++ b/files/pt-br/conflicting/learn/javascript/asynchronous/introducing/index.html @@ -0,0 +1,156 @@ +--- +title: Conceitos gerais da programação assíncrona +slug: conflicting/Learn/JavaScript/Asynchronous/Introducing +translation_of: Learn/JavaScript/Asynchronous/Concepts +original_slug: Learn/JavaScript/Asynchronous/Concepts +--- +
{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}}
+ +

Neste artigo, nós vamos ver um número de conceitos importantes relativos à programação assíncrona e como ela se parece em navegadores modernos e em JavaScript. Você deve entender estes conceitos antes de trabalhar com outros artigos neste módulo.

+ + + + + + + + + + + + +
Pré-requisitos:Conhecimentos básicos de informática e compreensão dos fundamentos de JavaScript.
Objetivo:Entender os conceitos básicos da programação assíncrona e como ela se manifesta em navegadores e JavaScript.
+ +

Assíncrono?

+ +

Normalmente, o código de um programa é executado de forma direta, com uma coisa acontecendo por vez. Se uma função depende do resultado de outra função, ela tem que esperar o retorno do resultado, e até que isso aconteça, o programa inteiro praticamente para de funcionar da perspectiva do usuário.

+ +

Usuários do Mac, por exemplo, conseguem ver isso como o cursor giratório em arco-íris (ou "beachball", como normalmente é chamado). Este cursor é o jeito do sistema operacional dizer: "o programa atual que você está usando teve que parar e esperar algo terminar de ser executado, e estava demorando tanto que fiquei preocupado se você estava pensando no que aconteceu."

+ +

Multi-colored macOS beachball busy spinner

+ +

Essa é uma situação frustrante, e não faz bom uso do poder de processamento do computador — especialmente em uma era em que computadores tem múltiplos núcleos de processamento disponíveis. Não há sentido em ficar esperando por algo quando você pode deixar outra tarefa ser executada em um núcleo de processador diferente e deixar que ele te avise quando terminar. Isso te permite fazer mais coisas por enquanto, o que é a base da programação assincrona. Depende do ambiente de programação que você está usando (navegadores da Web, no caso de desenvolvimento da Web) para fornecer APIs que permitem executar essas tarefas de forma assíncrona.

+ +

Bloqueio de código

+ +

Técnicas async (assíncronas) são muito úteis, principalmente na programação web. Quando um aplicativo web é executado em um navegador e executa um pedaço de código rigoroso sem retornar o controle para o navegador, ele pode parecer que travou. Isso é chamado de blocking; o navegador está bloqueado de continuar a manusear a entrada do usuário e de realizar outras tarefas até que o aplicativo web retorne o controle do processador.

+ +

Vamos dar uma olhadinha em alguns exemplos para que você entenda o blocking.

+ +

No nosso exemplo simple-sync.html (veja aqui), nós adicionamos um evento de click em um botão para que, quando clicado, ele executa uma tarefa pesada (calcula 10 milhões de datas e depois imprime a última delas no console) e depois adiciona um parágrafo no DOM:

+ +
const btn = document.querySelector('button');
+btn.addEventListener('click', () => {
+  let myDate;
+  for(let i = 0; i < 10000000; i++) {
+    let date = new Date();
+    myDate = date
+  }
+
+  console.log(myDate);
+
+  let pElem = document.createElement('p');
+  pElem.textContent = 'This is a newly-added paragraph.';
+  document.body.appendChild(pElem);
+});
+ +

Quando o exemplo for executado, abra seu console JavaScript e depois clique no botão  — você verá qua o parágrafo não aparece até que o programa termine de calcular as datas e imprimir a última no console. O código é executado na ordem em que ele aparece na fonte, e a operação seguinte só é executada depois que a primeira for terminada.

+ +
+

Nota: O exemplo anterior não é muito realistico. Você nunca calcularia 10 milhões de datas em um aplicativo real! Mas isso serve par te dar um apoio sobre o assunto.

+
+ +

No nosso segundo exemplo simple-sync-ui-blocking.html (veja aqui), nós simulamos algo mais realistico que você pode encontrar em uma página real. Nós bloqueamos a interatividade do usuário na renderização da UI. Neste exemplo, nós temos dois botões:

+ + + +
function expensiveOperation() {
+  for(let i = 0; i < 1000000; i++) {
+    ctx.fillStyle = 'rgba(0,0,255, 0.2)';
+    ctx.beginPath();
+    ctx.arc(random(0, canvas.width), random(0, canvas.height), 10, degToRad(0), degToRad(360), false);
+    ctx.fill()
+  }
+}
+
+fillBtn.addEventListener('click', expensiveOperation);
+
+alertBtn.addEventListener('click', () =>
+  alert('You clicked me!')
+);
+ +

Se você clicar no primeiro botão e imediatamente no segundo, você verá que a mensagem de alerta não aparece até que os círculos sejam totalmente renderizados. A primeira operação bloqueia a segunda até a sua finalização.

+ +
+

Nota: OK, no nosso caso, isso é ruim e estamos bloqueando o código de propósito, mas isso é um problema comum que desenvolvedores de aplicativos reais sempre tentam resolver.

+
+ +

E por quê isso acontece? A resposta é que o JavaScript é single threaded. E é neste ponto que precisamos introduzir a você o conceito de threads.

+ +

Threads

+ +

Uma thread é basicamente um único processo que um programa pode usar para concluir tarefas. Cada thread só pode fazer uma tarefa de cada vez:

+ +
Tarefa A --> Tarefa B --> Tarefa C
+ +

Cada tarefa será executada sequencialmente; uma tarefa tem que ser concluída antes que a próxima possa ser iniciada.

+ +

Como foi dito anteriormente, muitos computadores possuem múltiplos núcleos, para que possam fazer múltiplas coisas de uma vez só. Linguagens de programação que podem suportar múltiplas threads podem usar múltiplos processadores para concluir múltiplas tarefas simultâneamente:

+ +
Thread 1: Tarefa A --> Tarefa B
+Thread 2: Tarefa C --> Tarefa D
+ +

JavaScript é single threaded

+ +

JavaScript é tradicionalmente single-threaded. Mesmo com múltiplos núcleos de processamento, você só pode fazê-lo executar tarefas em uma única thread, chamada de main thread (thread principal). Nosso exemplo de cima é executado assim:

+ +
Main thread: Renderizar circulos no canvas --> Mostrar alert()
+ +

Depois de um tempo, o JavaScript ganhou algumas ferramentas para ajudar em tais problemas. As Web workers te permitem mandar parte do processamento do JavaScript para uma thread separada. Você geralmente usaria uma worker para executar um processo pesado para que a UI não seja bloqueada.

+ +
  Main thread: Tarefa A --> Tarefa C
+Worker thread: Tarefa pesada B
+ +

Com isso em mente, dê uma olhada em simple-sync-worker.html (veja aqui), com o seu console JavaScript aberto. Isso é uma nova versão do nosso exemplo que calcula 10 milhões de datas em uma thread worker separada. Agora, quando você clica no botão, o navegador é capaz de mostrar o parágrafo antes que as datas sejam terminadas. A primeira operação não bloqueia a segunda.

+ +

Código assíncrono

+ +

Web workers podem ser bem úteis, mas elas tem as suas limitações. Uma delas é que elas não são capazes de acessar a {{Glossary("DOM")}} — você não pode fazer com que uma worker faça algo diretamente para atualizar a UI. Nós não poderíamos renderizar nossos 1 milhão de círculos azuis na nossa worker; basicamente ela pode apenas fazer cálculos de números.

+ +

O segundo problema é que, mesmo que o código executado em uma worker não cause um bloqueio, ele ainda é um código síncrono. Isso se torna um problema quando uma função depende dos resultados de processos anteriores para funcionar. Considere os diagramas a seguir:

+ +
Main thread: Tarefa A --> Tarefa B
+ +

Nesse caso, digamos que a tarefa A está fazendo algo como pegar uma imagem do servidor e que a tarefa B faz algo com essa imagem, como colocar um filtro nela. Se você iniciar a tarefa A e depois tentar executar a tarefa B imediatamente, você obterá um erro, porque a imagem não estará disponível ainda.

+ +
  Main thread: Tarefa A --> Tarefa B --> |Tarefa D|
+Worker thread: Tarefa C ---------------> |      |
+ +

Neste caso, digamos que a tarefa D faz uso dos resultados das tarefas B e C. Se nós pudermos garantir que esses resultados estejam disponíveis ao mesmo tempo, então tudo talvez esteja bem, mas isso não é garantido. Se a tarefa D tentar ser executada quando um dos resultados não estiver disponível, ela retornará um erro.

+ +

Para consertarmos tais problemas, os browsers nos permitem executar certas operações de modo assíncrono. Recursos como Promises te permitem executar uma operação e depois esperar pelo resultado antes de executar outra operação: 

+ +
Main thread: Tarefa A                   Tarefa B
+    Promise:       |___operação async___|
+ +

Já que a operação está acontecendo em outro lugar, a main thread não está bloqueada enquanto a operação assíncrona está sendo processada.

+ +

Nós vamos começar a olhar em como podemos escrever código assíncrono no próximo artigo.

+ +

Conclusão

+ +

O design moderno de software gira em torno do uso de programação assíncrona, para permitir que os programas façam mais de uma coisa por vez. Ao usar APIs mais novas e mais poderosas, você encontrará mais casos em que a única maneira de fazer as coisas é assincronamente. Costumava ser difícil escrever código assíncrono. Ainda é preciso se acostumar, mas ficou muito mais fácil. No restante deste módulo, exploraremos ainda mais por que o código assíncrono é importante e como projetar o código que evita alguns dos problemas descritos acima.

+ +

Nesse módulo

+ + diff --git a/files/pt-br/conflicting/learn/javascript/asynchronous/promises/index.html b/files/pt-br/conflicting/learn/javascript/asynchronous/promises/index.html new file mode 100644 index 0000000000..04a2b90ba4 --- /dev/null +++ b/files/pt-br/conflicting/learn/javascript/asynchronous/promises/index.html @@ -0,0 +1,556 @@ +--- +title: Tornando mais fácil a programação assíncrona com async e await +slug: conflicting/Learn/JavaScript/Asynchronous/Promises +translation_of: Learn/JavaScript/Asynchronous/Async_await +original_slug: Learn/JavaScript/Asynchronous/Async_await +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Choosing_the_right_approach", "Learn/JavaScript/Asynchronous")}}
+ +

Adições mais recentes à linguagem JavaScript são as funções assíncronas e a palavra-chave await, adicionadas no ECMAScript 2017. Esses recursos atuam basicamente como um syntactic sugar em cima de promises, tornando o código assíncrono mais fácil de escrever e ler. Isso faz com que o código assíncrono pareça mais com o código síncrono old-school, então vale a pena aprender. Este artigo fornece o que você precisa saber.

+ + + + + + + + + + + + +
Pré-requisitos:Conhecimento básico de informática, uma compreensão razoável dos fundamentos do JavaScript, uma compreensão de código assíncrono em geral e promises.
Objetivo:Entender o uso do async/await.
+ +

O básico de async/await

+ +

Existem duas formas de usar async/await no seu código.

+ +

A palavra-chave async

+ +

Em primeiro lugar, temos a palavra-chave async, que você coloca antes de uma declaração de função para transformá-la em uma função assíncrona. Uma função assíncrona é uma função que espera a possibilidade de a palavra-chave await ser usada para invocar código assíncrono.

+ +

Experimente digitar as seguintes linhas no console JS do seu navegador.

+ +
function hello() { return "Hello" };
+    hello();
+ +

A funcão retorna "Hello" — nada de especial, certo?

+ +

Mas o que acontece se transformar-mos isso em uma função assíncrona? Tente o seguinte:

+ +
async function hello() { return "Hello" };
+      hello();
+ +

Ah. A invocação da função agora retorna uma promise. Isso é uma das características das funções assíncronas — seus valores de retorno são garantidos para serem convertidos em promises.

+ +

Você também pode criar uma expressão de função assíncrona, assim:

+ +
let hello = async function() { return "Hello" };
+      hello();
+ +

E você pode usar arrow functions:

+ +
let hello = async () => { return "Hello" };
+ +

Tudo isso faz basicamente a mesma coisa.

+ +

Para consumir o valor retornado quando a promise é finalizada, desde que esteja retornando uma promise, podemos usar um bloco .then():

+ +
hello().then((value) => console.log(value))
+ +

ou mesmo de forma simplificada

+ +
hello().then(console.log)
+ +

Como vimos no último artigo.

+ +

Então a palavra-chave async é adicionada nas funções para dizer a elas para retornar uma promise ao invés de diretamente retornar uma valor.

+
+ +

A palavra-chave await

+ +
+

A vantagem de uma função assíncrona só se torna aparente quando você a combina com a palavra-chave await. await só funciona dentro de funções assíncronas no código JavaScript regular, no entanto, pode ser usado por conta própria com JavaScript modules.

+ +

await pode ser colocado na frente de qualquer função assíncrona baseada em promise para pausar seu código nessa linha até que a promise seja resolvida e, em seguida, retornar o valor resultante.

+ +

Você pode usar await quando chamar qualquer função que retorne uma Promise, incluindo funções de API web.

+ +

Aqui está um exemplo comum:

+ +
async function hello() {
+      return greeting = await Promise.resolve("Hello");
+    };
+
+    hello().then(alert);
+ +

Com certeza, o exemplo acima não é muito útil, porém serve para ilustrar a sintaxe. Vamos seguir em frente e ver um exemplo real.

+
+ +

Reescrevendo código baseado em promises com async/await

+ +
+

Vejamos um exemplo simples de busca que vimos no artigo anterior:

+ +
fetch('coffee.jpg')
+    .then(response => {
+      if (!response.ok) {
+        throw new Error(`HTTP error! status: ${response.status}`);
+      }
+      return response.blob();
+    })
+    .then(myBlob => {
+      let objectURL = URL.createObjectURL(myBlob);
+      let image = document.createElement('img');
+      image.src = objectURL;
+      document.body.appendChild(image);
+    })
+    .catch(e => {
+      console.log('There has been a problem with your fetch operation: ' + e.message);
+    });
+ +

Por enquanto, você precisa ter um entendimento razoável das promises e como elas funcionam, mas vamos converter isso para usar async/await e ver o quão simples as coisas se tornam:

+ +
async function myFetch() {
+      let response = await fetch('coffee.jpg');
+
+      if (!response.ok) {
+        throw new Error(`HTTP error! status: ${response.status}`);
+      }
+
+      let myBlob = await response.blob();
+
+      let objectURL = URL.createObjectURL(myBlob);
+      let image = document.createElement('img');
+      image.src = objectURL;
+      document.body.appendChild(image);
+    }
+
+    myFetch()
+    .catch(e => {
+      console.log('There has been a problem with your fetch operation: ' + e.message);
+    });
+ +

Isto faz o código muito mais simples and fácil de entender — sem mais blocos .then() em todo lugar!

+ +

Visto que a palavra-chave async transforma a funcão em uma promise, você pode refatorar seu código para usar uma abordagem de promises e await, trazendo a segunda metade da funcão para um novo bloco e torná-la mais flexível:

+ +
async function myFetch() {
+      let response = await fetch('coffee.jpg');
+      if (!response.ok) {
+        throw new Error(`HTTP error! status: ${response.status}`);
+      }
+      return await response.blob();
+
+    }
+
+    myFetch().then((blob) => {
+      let objectURL = URL.createObjectURL(blob);
+      let image = document.createElement('img');
+      image.src = objectURL;
+      document.body.appendChild(image);
+    }).catch(e => console.log(e));
+ +

Você pode tentar fazer o exemplo sozinho, ou rodar o nosso exemplo ao vivo (veja também ocódigo-fonte).

+
+ +

Mas como isso funciona?

+ +
+

Você notará que empacotamos o código dentro de uma função, e incluímos a palavra-chave async antes da palavra-chavefunction. Isso é necessário — você tem que criar uma função assíncrona para definir o bloco de código no qual você executará seu código assíncrono; como falamos mais cedo, await só funciona dentro de funções assíncronas.

+ +

Dentro da definição da função myFetch() você pode ver que o código se parece muito à versão anterior com promise, , mas tem algumas diferenças. Ao invés de precisar encadear um bloco .then() no final de cada método baseado em promise, você apenas adiciona a palavra-chave await antes de cada chamada de método, e então atribui o resultado a variável. A palavra-chave await faz com que o JavaScript pause seu código em tempo de execução nesta linha, não permitindo mais  nenhum código ser executado nesse meio tempo até que a chamada de função assíncrona retorne seu resultado — muito útil se o código subsequente depender desse resultado!

+ +

Assim que estiver completo, seu código continua a ser executado começando na próxima linha. Por exemplo:

+ +
let response = await fetch('coffee.jpg');
+ +

A resposta retornada pela promise fetch() preenchida é atribuída a variável response quando a resposta estiver disponível, e o parser pausa nesta linha até que isso ocorra. Uma vez que a resposta está disponível, o parser move para a próxima linha, o qual cria o Blob fora dele. Esta linha também invoca um método assíncrono baseado em promise, assim podemos usar await aqui também. Quando o resultado da operação retorna, retornamos isso fora da funcão myFetch().

+ +

Isso significa que quando nós chamamos a função myFetch(), isso retorna uma promise, então podemos encadear um .then() no final, dentro do qual lidamos com a exibição do blob na tela.

+ +

Provavelmente você já está pensando "isso é realmente demais!", e você está certo — menos blocos .then() para envolver o código, e quase sempre se parece com um código síncrono, por isso é muito intuitivo.

+
+ +

Adicionando tratamento de erros

+ +
+

E se você deseja adicionar tratamento de erros, você tem algumas opções.

+ +

Você pode usar uma estrutura try...catch síncrona com async/await. Este exemplo se expande da primeira versão do código que mostramos acima:

+ +
async function myFetch() {
+      try {
+        let response = await fetch('coffee.jpg');
+
+        if (!response.ok) {
+          throw new Error(`HTTP error! status: ${response.status}`);
+        }
+        let myBlob = await response.blob();
+        let objectURL = URL.createObjectURL(myBlob);
+        let image = document.createElement('img');
+        image.src = objectURL;
+        document.body.appendChild(image);
+
+      } catch(e) {
+        console.log(e);
+      }
+    }
+
+    myFetch();
+ +

Ao bloco catch() {} é passado um objeto de erro, qual nós chamamos e; agora podemos registrar isso no console, e isso nos fornecerá uma mensagem de erro detalhada mostrando onde o erro foi gerado no código.

+ +

Se você quiser usar a segunda versão (refatorada) do código que mostramos acima, seria melhor apenas continuar a abordagem híbrida e encadear um bloco .catch() no final da chamada .then(), dessa forma:

+ +
async function myFetch() {
+      let response = await fetch('coffee.jpg');
+      if (!response.ok) {
+        throw new Error(`HTTP error! status: ${response.status}`);
+      }
+      return await response.blob();
+
+    }
+
+    myFetch().then((blob) => {
+      let objectURL = URL.createObjectURL(blob);
+      let image = document.createElement('img');
+      image.src = objectURL;
+      document.body.appendChild(image);
+    })
+    .catch((e) =>
+      console.log(e)
+    );
+ +

Isso ocorre porque o bloco .catch() vai pegar os erros que ocorrem em ambos, na chamada de função com async e com cadeia de promises. Se você usou bloco try/catch aqui, você ainda pode obter erros não tratados na função myFetch() quando ela for chamada.

+ +

Você pode encontrar esses dois exemplos no GitHub:

+ + +
+ +

Esperando um Promise.all()

+ +
+

async/await é construído em cima de promises, por isso é compatível com todos os recursos oferecidos por promises. Isso inclui Promise.all() — você pode esperar felizmente uma chamada Promise.all() para obter todos os resultados retornados em uma variável de uma forma que se pareça com um código síncrono simples. De novo, vamos voltar para um exemplo que vimos em nosso artigo anterior. Mantenha-o aberto em uma guia separada para que você possa comparar e contrastar com a nova versão mostrada abaixo.

+ +

Convertendo este para async/await (veja demonstração ao vivo e código-fonte), isso agora parece assim:

+ +
async function fetchAndDecode(url, type) {
+      let response = await fetch(url);
+
+      let content;
+
+      if (!response.ok) {
+        throw new Error(`HTTP error! status: ${response.status}`);
+      } else {
+        if(type === 'blob') {
+          content = await response.blob();
+        } else if(type === 'text') {
+          content = await response.text();
+        }
+      }
+
+      return content;
+
+
+    }
+
+    async function displayContent() {
+      let coffee = fetchAndDecode('coffee.jpg', 'blob');
+      let tea = fetchAndDecode('tea.jpg', 'blob');
+      let description = fetchAndDecode('description.txt', 'text');
+
+      let values = await Promise.all([coffee, tea, description]);
+
+      let objectURL1 = URL.createObjectURL(values[0]);
+      let objectURL2 = URL.createObjectURL(values[1]);
+      let descText = values[2];
+
+      let image1 = document.createElement('img');
+      let image2 = document.createElement('img');
+      image1.src = objectURL1;
+      image2.src = objectURL2;
+      document.body.appendChild(image1);
+      document.body.appendChild(image2);
+
+      let para = document.createElement('p');
+      para.textContent = descText;
+      document.body.appendChild(para);
+    }
+
+    displayContent()
+    .catch((e) =>
+      console.log(e)
+    );
+ +

Você notará que a função fetchAndDecode() foi convertida facilmente em uma função assíncrona com apenas algumas alterações. Veja a linha do Promise.all():

+ +
let values = await Promise.all([coffee, tea, description]);
+ +

Usando await aqui podemos obter todos os resultados das três promises retornadas no array values, quando todos eles estão disponíveis, de uma forma que se parece muito com o código síncrono. Tivemos que envolver todo o código em uma nova função assíncrona, displayContent(), e não reduzimos o código em muitas linhas, mas ser capaz de mover a maior parte do código para fora do bloco .then() fornece uma simplificação agradável e útil, deixando-nos com um programa muito mais legível.

+ +

Para tratamento de erros, nós incluímos um bloco .catch() no nossa chamada displayContent(); isso vai lidar com os erros que ocorrem em ambas as funções.

+ +
+

Nota: Também é possível usar um bloco finally síncrono na função assíncrona, no lugar de um bloco assíncrono.finally(), para mostrar um relatório final sobre como foi a operação — você pode ver isso em ação no nosso exemplo ao vivo (veja também o código-fonte).

+
+
+ +

Tratando lentidão com async/await

+ +
+

Async/await faz seu código parecer síncrono e, de certa forma, faz com que se comporte de maneira mais síncrona. A palavra-chave await bloqueia a execução de todo o código que o segue até que a promise seja cumprida, exatamente como faria com uma operação síncrona. Ele permite que outras tarefas continuem sendo executadas enquanto isso, mas o código com await é bloqueado. Por exemplo:

+ +
async function makeResult(items) {
+      let newArr = [];
+      for(let i=0; i < items.length; i++) {
+        newArr.push('word_'+i);
+      }
+      return newArr;
+    }
+
+    async function getResult() {
+      let result = await makeResult(items); // Blocked on this line
+      useThatResult(result); // Will not be executed before makeResult() is done
+    }
+
+    
+ +

Como resultado, seu código pode ser retardado por um número significativo de promises aguardadas acontecendo uma após a outra. Cada await vai esperar que o anterior termine, ao passo que, na verdade, o que você pode querer é que as promises comecem a ser processadas simultaneamente, como fariam se não estivéssemos usando async/await.

+ +

Vejamos esses dois exemplos — slow-async-await.html (veja código-fonte) e fast-async-await.html (veja código-fonte). Ambos começam com uma função promise personalizada que simula um processo assíncrono com uma chamada setTimeout():

+ +
function timeoutPromise(interval) {
+      return new Promise((resolve, reject) => {
+        setTimeout(function(){
+          resolve("done");
+        }, interval);
+      });
+    };
+ +

Cada um deles inclui uma função assíncrona timeTest() que espera três chamadas timeoutPromise():

+ +
async function timeTest() {
+      ...
+    }
+ +

Cada um termina registrando um horário de início, vendo quanto tempo a promise timeTest() leva para completar, em seguida, registrando um horário de término e relatando quanto tempo a operação levou no total:

+ +
let startTime = Date.now();
+    timeTest().then(() => {
+      let finishTime = Date.now();
+      let timeTaken = finishTime - startTime;
+      alert("Time taken in milliseconds: " + timeTaken);
+    })
+ +

Isso é a função timeTest() que difere em cada caso.

+ +

No exemplo slow-async-await.html, timeTest() se parece com isso:

+ +
async function timeTest() {
+      await timeoutPromise(3000);
+      await timeoutPromise(3000);
+      await timeoutPromise(3000);
+    }
+ +

Aqui esperamos diretamente todas as três chamadas timeoutPromise(), fazendo cada uma a cada 3 segundos. Cada chamada subsequente é forçada a esperar até que a última termine — se você executar o primeiro exemplo, você verá a caixa de alerta relatando um tempo total de execução de cerca de 9 segundos.

+ +

No exemplo fast-async-await.html, timeTest() se parece com isso:

+ +
async function timeTest() {
+      const timeoutPromise1 = timeoutPromise(3000);
+      const timeoutPromise2 = timeoutPromise(3000);
+      const timeoutPromise3 = timeoutPromise(3000);
+
+      await timeoutPromise1;
+      await timeoutPromise2;
+      await timeoutPromise3;
+    }
+ +

Aqui nós armazenamos os três objetos Promise em variáveis, que tem o efeito de desencadear seus processos associados, todos rodando simultaneamente.

+ +

A seguir, aguardamos seus resultados — porque todas as promises começaram a ser processadas essencialmente ao mesmo tempo, as promises serão cumpridas todas ao mesmo tempo; ao executar o segundo exemplo, você verá a caixa de alerta relatando um tempo total de execução de pouco mais de 3 segundos!

+
+ +

Tratamento de erros

+ +
+

Há um problema com o padrão acima, no entanto — pode levar a erros não tratados.

+ +

Vamos atualizar os exemplos anteriores, desta vez adicionando uma promise rejeitada e uma declaração catch no final:

+ +
function timeoutPromiseResolve(interval) {
+      return new Promise((resolve, reject) => {
+        setTimeout(function(){
+          resolve("successful");
+        }, interval);
+      });
+    };
+
+    function timeoutPromiseReject(interval) {
+      return new Promise((resolve, reject) => {
+        setTimeout(function(){
+          reject("error");
+        }, interval);
+      });
+    };
+
+    async function timeTest() {
+      await timeoutPromiseResolve(5000);
+      await timeoutPromiseReject(2000);
+      await timeoutPromiseResolve(3000);
+    }
+
+    let startTime = Date.now();
+    timeTest().then(() => {})
+    .catch(e => {
+      console.log(e);
+      let finishTime = Date.now();
+      let timeTaken = finishTime - startTime;
+      alert("Time taken in milliseconds: " + timeTaken);
+    })
+ +

No exemplo acima, o erro é tratado corretamente, e o alerta aparece após aproximadamente 7 segundos.

+ +

Agora no segundo padrão:

+ +
function timeoutPromiseResolve(interval) {
+      return new Promise((resolve, reject) => {
+        setTimeout(function(){
+          resolve("successful");
+        }, interval);
+      });
+    };
+
+    function timeoutPromiseReject(interval) {
+      return new Promise((resolve, reject) => {
+        setTimeout(function(){
+          reject("error");
+        }, interval);
+      });
+    };
+
+    async function timeTest() {
+      const timeoutPromiseResolve1 = timeoutPromiseResolve(5000);
+      const timeoutPromiseReject2 = timeoutPromiseReject(2000);
+      const timeoutPromiseResolve3 = timeoutPromiseResolve(3000);
+
+      await timeoutPromiseResolve1;
+      await timeoutPromiseReject2;
+      await timeoutPromiseResolve3;
+    }
+
+    let startTime = Date.now();
+    timeTest().then(() => {
+    }).catch(e => {
+      console.log(e);
+      let finishTime = Date.now();
+      let timeTaken = finishTime - startTime;
+      alert("Time taken in milliseconds: " + timeTaken);
+    })
+ +

Neste exemplo, temos um erro não tratado no console (depois de 2 segundos), e o alerta aparece após aproximadamente 5 segundos.

+ +

Para iniciar as promises em paralelo e detectar o erro corretamente, nós poderíamos usar Promise.all(), como discutido anteriormente:

+ +
function timeoutPromiseResolve(interval) {
+      return new Promise((resolve, reject) => {
+        setTimeout(function(){
+          resolve("successful");
+        }, interval);
+      });
+    };
+
+    function timeoutPromiseReject(interval) {
+      return new Promise((resolve, reject) => {
+        setTimeout(function(){
+          reject("error");
+        }, interval);
+      });
+    };
+
+    async function timeTest() {
+      const timeoutPromiseResolve1 = timeoutPromiseResolve(5000);
+      const timeoutPromiseReject2 = timeoutPromiseReject(2000);
+      const timeoutPromiseResolve3 = timeoutPromiseResolve(3000);
+
+      const results = await Promise.all([timeoutPromiseResolve1, timeoutPromiseReject2, timeoutPromiseResolve3]);
+      return results;
+    }
+
+    let startTime = Date.now();
+    timeTest().then(() => {
+    }).catch(e => {
+      console.log(e);
+      let finishTime = Date.now();
+      let timeTaken = finishTime - startTime;
+      alert("Time taken in milliseconds: " + timeTaken);
+    })
+ +

Neste exemplo, o erro é tratado corretamente após aproximadamente 2 segundos e também vemos o alerta após aproximadamente 2 segundos.

+ +

A Promise.all() rejeita quando qualquer uma das promises de entrada é rejeitada. Se você deseja que todas as promises sejam cumpridas e, em seguida, usar alguns de seus valores retornados, mesmo quando alguns deles são rejeitados, você pode usar Promise.allSettled().

+
+ +

Async/await em métodos de classe

+ +
+

Como nota final, antes de prosseguirmos, você pode até adicionar async na frente de métodos de classe / objeto para fazê-los retornar promises, e await promises dentro deles. Dê uma olhada no artigo Código de classe ES que vimos em nosso JavaScript orientado a objetos. em seguida, olhe para nossa versão modificada com um método async:

+ +
class Person {
+      constructor(first, last, age, gender, interests) {
+        this.name = {
+          first,
+          last
+        };
+        this.age = age;
+        this.gender = gender;
+        this.interests = interests;
+      }
+
+      async greeting() {
+        return await Promise.resolve(`Hi! I'm ${this.name.first}`);
+      };
+
+      farewell() {
+        console.log(`${this.name.first} has left the building. Bye for now!`);
+      };
+    }
+
+    let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
+ +

O primeiro método da classe agora pode ser usado assim:

+ +
han.greeting().then(console.log);
+
+ +

Suporte de navegador

+ +
+

Uma consideração ao decidir se deve usar async/await é o suporte para navegadores mais antigos. Eles estão disponíveis em versões modernas da maioria dos navegadores, o mesmo que promises; os principais problemas de suporte vêm com o Internet Explorer e o Opera Mini.

+ +

Se você deseja usar async/await, mas está preocupado com o suporte a navegadores mais antigos, pode considerar o uso da biblioteca BabelJS — isso permite que você escreva seus aplicativos usando o JavaScript mais recente e deixe Babel descobrir quais mudanças, se houver, são necessárias para os navegadores de seu usuário. Ao encontrar um navegador que não suporta async/await, o polyfill do Babel pode fornecer automaticamente substitutos que funcionam em navegadores mais antigos.

+
+ +

Conclusão

+ +

E aí está - async/await fornecem uma maneira agradável e simplificada de escrever código assíncrono que é mais simples de ler e manter. Mesmo com o suporte do navegador sendo mais limitado do que outros mecanismos de código assíncrono no momento da escrita, vale a pena aprender e considerar o uso, agora e no futuro.

+ +

{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Choosing_the_right_approach", "Learn/JavaScript/Asynchronous")}}

+ +

Neste módulo

+ + + diff --git a/files/pt-br/conflicting/learn/javascript/asynchronous_ae5a561b0ec11fc53c167201aa8af5df/index.html b/files/pt-br/conflicting/learn/javascript/asynchronous_ae5a561b0ec11fc53c167201aa8af5df/index.html new file mode 100644 index 0000000000..07c4f43719 --- /dev/null +++ b/files/pt-br/conflicting/learn/javascript/asynchronous_ae5a561b0ec11fc53c167201aa8af5df/index.html @@ -0,0 +1,625 @@ +--- +title: Timeouts e intervalos +slug: conflicting/Learn/JavaScript/Asynchronous_ae5a561b0ec11fc53c167201aa8af5df +translation_of: Learn/JavaScript/Asynchronous/Timeouts_and_intervals +original_slug: Learn/JavaScript/Asynchronous/Timeouts_and_intervals +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}
+ +

Este tutorial é sobre os métodos tradicionais que o JavaScript tem disponíveis para executar códigos assíncronamente depois que um dado período de tempo tenha passado, ou em um intervalo (um número de segundos por segundo), discute suas utilidades e considera seus problemas.

+ + + + + + + + + + + + +
Pré-requisitos:Entendimento básico sobre informáticas e fundamentos do JavaScript.
Objetivo:Entender loops e intervalos assíncronos e para o que eles servem.
+ +

Introdução

+ +

Por um longo tempo, a plataforma web tem oferecido à programadores JavaScript um número de funções que permitem que eles executem código assíncronamente depois de um determinado intervalo de tempo, e executar um bloco de código de modo assíncrono repetidamente até que você o mande parar.

+ +

Essas funções são:

+ +
+
setTimeout()
+
Executa um bloco específico uma vez depois de um determinado tempo
+
setInterval()
+
Executa um bloco específico repetidamente com um intervalo fixo entre cada chamada.
+
requestAnimationFrame()
+
Uma versão moderna de setInterval(). Ela executa um  bloc de código específico antes do navegador renderizar a tela novamento, permitindo que seja executada em uma taxa de quadros adequada, independentemente do ambiente em que está sendo executado.
+
+ +

O código executado por estas funções é executado na main thread (depois do dado intervalo).

+ +
+

É importante saber que você pode (e irá) executar outros códigos antes que uma chamada setTimeout() é executada, ou entre iterações de setInterval(). Dependendo de como essas operações são intensas, elas podem atrasar o seu código async ainda mais, já que o código async só é executado depois que a main thread terminar seu processamento (ou seja, quando a fila estiver vazia). Você aprenderá mais sobre isso enquanto fazemos nosso progresso neste artigo.

+
+ +

De qualquer forma, essas funções são usadas para executar animações constantes e outros processamentos em um web site ou aplicação. Nas seções a seguir, nós vamos te mostrar como elas podem ser usadas.

+ +

setTimeout()

+ +

Como foi dito anteriormente, o setTimeout() executa um bloco de código particular depois que um determinado período de tempo passou. Ele toma os seguintes parâmetros:

+ + + +
+

NOTA: O tempos especificafo não é o tempo garantido de execução, mas sim o tempo míniimo de execução. As callback que você passa para essas funções não podem ser executadas até que a main thread esteja vazia.

+ +

Como consequência, códigos como setTimeout(fn, 0) serão executados assim que a fila estiver vazia, não imediatamente. Se você executar código como setTimeout(fn, 0) e depois imediatamente executar um loop que conta de 1 a 10 bilhões, sua callback será executada depois de alguns segundos.

+
+ +

No exemplo a seguir, o navegador vai esperar dois segundos antes de executar a função anônima, e depois vai mostrar a mensagem de alerta (veja aqui, e veja o código):

+ +
let myGreeting = setTimeout(function() {
+  alert('Hello, Mr. Universe!');
+}, 2000)
+ +

As funções especificadas não tem que  ser anônimas. Você pode dar o nome da função, e até mesmo definir ela em outro lugar e passar uma referência para o timeout setTimeout(). As versões a seguir do código são equivalentes à primeira:

+ +
// With a named function
+let myGreeting = setTimeout(function sayHi() {
+  alert('Hello, Mr. Universe!');
+}, 2000)
+
+// With a function defined separately
+function sayHi() {
+  alert('Hello Mr. Universe!');
+}
+
+let myGreeting = setTimeout(sayHi, 2000);
+ +

Isso pode ser útil se você tem uma função que precisa ser chamada de um timeout e também em resposta à um evento, por exemplo. Mas também pode servir para manter seu código organizado, especialmente se a callback timetout é mais do que algumas linhas de código.

+ +

setTimeout() retorna um valor identificador que pode ser usado para se referir ao timeout depois, como em quando você que pará-lo. Veja {{anch("Cancelando timetous")}} (abaixo) e aprenda como fazer isso.

+ +

Passando parâmetros para uma função setTimeout()

+ +

Quaisquer parâmetros que você quiser passar para a função sendo executada dentro do setTimeout() devem ser passados como parâmetros adicionais no final da lista.

+ +

Por exemplo, você pode mudar a função anterior para que ela diga oi para qualquer nome que foi passada para ela:

+ +
function sayHi(who) {
+  alert(`Hello ${who}!`);
+}
+ +

Agora, você pode passar o nome da pessoa no setTimeout() como um terceiro parâmetro:

+ +
let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');
+ +

Cancelando timeouts

+ +

Finalmente, se um timeout foi criado, você pode cancelá-lo antes que o tempo especificado tenha passado chamando clearTimeout(), passando para o identificador a chamada setTimeout() como um parâmetreo. então para cancelar o timeout acima, você fará isso:

+ +
clearTimeout(myGreeting);
+ +
+

Nota: Veja greeter-app.html para uma demonstração mais desenvolvida que te permite colocar o nome da pessoa a dizer oi em um formulário, e cancelar a saudação usando um botão separado (veja aqui o código fonte).

+
+ +

setInterval()

+ +

setTimeout() funciona perfeitamento quando você precisa executar algum código depois de um período de tempo. Mas o que acontece quando voc~e precisa executar o código de novo e de novo — por exemplo, no caso de uma animação?

+ +

É aí que o setInterval() entra. Ele funciona de uma maneira muito similar à setTimeout(), exceto que a função que você passar como primeiro parâmetro é executada repetidamente em não menos que um número determinado de milissegundos dado no segundo parâmetro, ao invés de apenas uma vez. Você também pode passar qualquer parâmetro sendo executado como um parâmetro subsequente da chamada de setInterval().

+ +

Vamos dar uma olhada em um exemplo. A função a seguir cria um novo objeto Date(), tira uma string de tempo usando toLocaleTimeString(), e depois a mostra naUI. Em seguida, ela executa a função uma vez por segundo usando setInterval(), criando o efeito de um relógio digital que é atualizado uma vez por segundo (veja aqui, e também veja o código):

+ +
function displayTime() {
+   let date = new Date();
+   let time = date.toLocaleTimeString();
+   document.getElementById('demo').textContent = time;
+}
+
+const createClock = setInterval(displayTime, 1000);
+ +

Assim como o setTimeout(), o setInterval() também retorna um valor identificador que você pode usar depois para cancelar o intervalo.

+ +

Cancelando intervalos

+ +

setInterval() continua sua execução para sempre, a menos que você faça algo sobre isso. Você provavelmente quer um jeito de parar tais tarefas, do contrário você pode acabar com error quando o navegador não puder completar outras versões futuras da tarefa, ou se a animação acabar. Você pode fazer isso do mesmo jeito que você para timeouts — passando o identificador retornado por setInterval() para a função clearInterval():

+ +
const myInterval = setInterval(myFunction, 2000);
+
+clearInterval(myInterval);
+ +

Aprendizado ativo: Criando seu próprio cronômetro!

+ +

Com tudo isso dito, nós temos um desafio para você. Faça uma cópia do nosso exemplo setInterval-clock.html, e o modifique para criar seu próprio cronômetro.

+ +

Você precisa mostrar um tempo na tela como antes, mas nesse exemplo você vai precisar de:

+ + + +

Here's a few hints for you:

+ + + +
+

Note: If you get stuck, you can find our version here (see the source code also).

+
+ +

Coisas para se manter em mente sobre o setTimeout() e o setInterval()

+ +

Existem algumas coisinhas que devemos sempre lembrar quando estamos trabalhando com setTimeout() esetInterval():

+ +

Timeouts recursivos

+ +

Há outra maneira de usar o  setTimeout(): você pode chamá-lo recusivamnete para executar o mesmo código repetidas vezes, ao invés de usar o setInterval().

+ +

O exemplo abaixo usa um setTimeout() recursivo para executar a função passada a cada 100 millissegundos:

+ +
let i = 1;
+
+setTimeout(function run() {
+  console.log(i);
+  i++;
+  setTimeout(run, 100);
+}, 100);
+ +

Compare the above example to the following one — this uses setInterval() to accomplish the same effect:

+ +
let i = 1;
+
+setInterval(function run() {
+  console.log(i);
+  i++
+}, 100);
+ +

Qual a diferença entre o setTimeout() recursivo e o setInterval()?

+ +

A diferença entre as duas versões é bem sútil.

+ + + +

Quando seu código tem o potencial para levar mais tempo do que lhe foi atribuido, é melhor usar o setTimeout() recursivo — isso irá manter o intervalo de tempo constant entre execuções independente do quanto tempo o código levar para ser executado, e você não terá erros.

+ +

Timeouts imediatos

+ +

Usar zero como o valor para setTimeout() faz a execução da callback ser o mais rápido o possível, mas apenas depois que a main thread for terminada.

+ +

Por exemplo, o código abaixo (veja funcionar aqui) mostra um alert que contém um "Hello", depois um  alert que contém "World" assim que você clicar em OK no primeiro alerta.

+ +
setTimeout(function() {
+  alert('World');
+}, 0);
+
+alert('Hello');
+ +

Isso pode ser útil em casos onde você quer fazer um bloco de código ser executado assim que a main thread acabar o seu processamento — colocar no loop de eventos async, assim ele vai ser executado logo depois.

+ +

Cancelando com clearTimeout() ou clearInterval()

+ +

clearTimeout() e clearInterval() usam a mesma lista de entradas para cancelamento. Isso significa que você pode usar os dois para cancelar um setTimeout() ou setInterval().

+ +

Mas mesmo assim, você deve usar o clearTimeout() para entradas setTimeout() e clearInterval() para entradas setInterval(). Isso evita confusões.

+ +

requestAnimationFrame()

+ +

requestAnimationFrame() é uma função de loop especializada criada para executar animações com eficiência no navegador. Ela é basicamente a versão moderna de setInterval() — ela executa um bloco de código específico antes que o navegador renove o display, permitindo que uma animação seja executada em um framerate adequado independente do ambiente em que está sendo executada.

+ +

Ela foi criada em resposta à problemas ocorridos com setInterval(), que por exemplo não roda em uma taxa de quadros otimizada para o dispositivo, e às vezes diminui os frames, continua a rodar mesmo se a guia não esiver ativa ou se a animação for rolada para fora da página, etc.

+ +

(Leia mais sobre isso em CreativeJS.)

+ +
+

Nota: Você pode encontrar exemplos do uso de requestAnimationFrame() em outros lugares do curso — por exemplo em Drawing graphics, e Object building practice.

+
+ +

O método toma como argumentos uma callback a  ser invocada antes da renovação. Esse é o padrão geral que você verá usado em:

+ +
function draw() {
+   // Drawing code goes here
+   requestAnimationFrame(draw);
+}
+
+draw();
+ +

A ideia é definir uma função em que sua animação é atualizada (e.g. seus spritas se movem, a pontuação é atualizada, dados são recarregados, etc). Depois, você inicia o processo. No final do bloco da função você chama  requestAnimationFrame() com a referência da função passada como parâmetro, e isso instrui o navegador a chamar a função de novo na próxima renovação. Isso é executado continuamente, já que o código está chamando requestAnimationFrame() recursivamente.

+ +
+

Nota: Se você quer realizar algum tipo de animação na DOM constantemente, Animações CSS são provavelemente mais rápidas. elas são calculadas diretamente pelo código interno do navegador, ao invés de JavaScript.

+ +

Se, no entanto, você está fazendo algo mais complexo e envolvendo objetos que não são diretamente assessados da DOM (como 2D Canvas API ou objetos WebGL), requestAnimationFrame() é a melhor opção na maioria dos casos

+
+ +

Qual a velocidade da sua animação?

+ +

A suavidade da sua animação é diretamente dependente na frame rate da sua animação e é medida em frames per second (fps). The smoothness of your animation is directly dependent on your animation's frame rate and it is measured in frames per second (fps). Quanto maior esse número, mais suave será a sua animação, até certo ponto.

+ +

Já que a maioria das tela tem uma taxa de atualização de 60Hz, a frame rate mais rápida que você pode ter é de 60fps quando trabalhando com web browsers. No entanto, mais frames significa mais processamento, o que pode ser causar uma queda de quadros e travamento.

+ +

Se você tem um monitos com uma taxa de atualização de 60Hz e você quer atingir 60FPS você tem pelo menos 16.7 milissegundos (1000 / 60) para executar sua animação em cada frame. Isso é um lembrete de que você vai precisar estar atento à quantidade de código que você vai tentar executar em cada iteração do loop de animação.

+ +

requestAnimationFrame() sempre tenta ficar o mais próximo possível de 60 FPS. Às vezes, isso não é possível — se você tem uma animação bem complexa e você está executando ela em um computador lento, sua frame rate será menor. Em todos os casos, o requestAnimationFrame() sempre vai fazer o melhor que pode com o que ele tem dísponivel.

+ +

Como o requestAnimationFrame() se diferencia de setInterval() e setTimeout()?

+ +

Vamos falar um pouco sobre como o método requestAnimationFrame() se diferencia dos outros métodos vistos anteriormente. Olhando com o código anterior:

+ +
function draw() {
+   // Drawing code goes here
+   requestAnimationFrame(draw);
+}
+
+draw();
+ +

Vamos ver isso usando o setInterval():

+ +
function draw() {
+   // Drawing code goes here
+}
+
+setInterval(draw, 17);
+ +

Como foi dito anteriormente, você não especifica um intervalo de tempo para requestAnimationFrame(). O método se executa o mais rápido e suave o possível nas condições atuais. O navegador também não perde tempo executando uma animação se ela está fora da tela por algum motivo, etc.

+ +

setInterval(), por outro lado, exige que um  intervalo de tempo seja especificado. Nós chegamos ao valor final de 17 por meio da formula 1000 milliseconds / 60Hz, e depois arredondamos o resultado. Arredondar é uma boa ideia; se você tivesse arredondado para baixo, o navegador pode tentar executar a animação mais rápido do que 60  FPS, e não faria nenhuma diferênça na suavidade da animação de qualquer forma. Como foi dito antes, 60Hz é a taxa de atualização padrão.

+ +

Incluindo um timestamp

+ +

A callback passada para a função requestAnimationFrame() pode ser dada um parâmetro támbem: um valor timestamp, que representa o tempo desde que o  requestAnimationFrame() começou a rodar.

+ +

Isso é útil, permite que você execute coisas em  um tempo específico e em passo constante, independente do quão rápido ou lento é o seu dispositivo. O padão geral que você usaria se parece um pouco com isso:

+ +
let startTime = null;
+
+function draw(timestamp) {
+    if (!startTime) {
+      startTime = timestamp;
+    }
+
+   currentTime = timestamp - startTime;
+
+   // Do something based on current time
+
+   requestAnimationFrame(draw);
+}
+
+draw();
+ +

Suporte do navegador

+ +

requestAnimationFrame() é suportado em navegadores mais recentes do que setInterval()/setTimeout().  Curiosamente, está disponível no Internet Explorer 10 e além.

+ +

Então, você não precisa dar suporte para versões mais velhas do IE, não há poruqe não usar o  requestAnimationFrame().

+ +

Um exemplo simples

+ +

Enough with the theory! Let's build your own personal requestAnimationFrame() example. You're going to create a simple "spinner animation"—the kind you might see displayed in an app when it is busy connecting to the server, etc.

+ +
+

Note: In a real world example, you should probably use CSS animations to run this kind of simple animation. However, this kind of example is very useful to demonstrate requestAnimationFrame() usage, and you'd be more likely to use this kind of technique when doing something more complex such as updating the display of a game on each frame.

+
+ +
    +
  1. +

    Grab a basic HTML template (such as this one).

    +
  2. +
  3. +

    Put an empty {{htmlelement("div")}} element inside the {{htmlelement("body")}}, then add a ↻ character inside it. This is circular arrow character will act as our spinner for this example.

    +
  4. +
  5. +

    Apply the following CSS to the HTML template (in whatever way you prefer). This sets a red background on the page, sets the <body> height to 100% of the {{htmlelement("html")}} height, and centers the <div> inside the <body>, horizontally and vertically.

    + +
    html {
    +  background-color: white;
    +  height: 100%;
    +}
    +
    +body {
    +  height: inherit;
    +  background-color: red;
    +  margin: 0;
    +  display: flex;
    +  justify-content: center;
    +  align-items: center;
    +}
    +
    +div {
    +  display: inline-block;
    +  font-size: 10rem;
    +}
    +
  6. +
  7. +

    Insert a {{htmlelement("script")}} element just above the </body> tag.

    +
  8. +
  9. +

    Insert the following JavaScript inside your <script> element. Here, you're storing a reference to the <div> inside a constant, setting a rotateCount variable to 0, setting an uninitialized variable that will later be used to contain a reference to the requestAnimationFrame() call, and setting a startTime variable to null, which will later be used to store the start time of the requestAnimationFrame().

    + +
    const spinner = document.querySelector('div');
    +let rotateCount = 0;
    +let startTime = null;
    +let rAF;
    +
    +
  10. +
  11. +

    Below the previous code, insert a draw() function that will be used to contain our animation code, which includes the timestamp parameter:

    + +
    function draw(timestamp) {
    +
    +}
    +
  12. +
  13. +

    Inside draw(), add the following lines. They will define the start time if it is not defined already (this will only happen on the first loop iteration), and set the rotateCount to a value to rotate the spinner by (the current timestamp, take the starting timestamp, divided by three so it doesn't go too fast):

    + +
      if (!startTime) {
    +   startTime = timestamp;
    +  }
    +
    +  rotateCount = (timestamp - startTime) / 3;
    +
    +
  14. +
  15. +

    Below the previous line inside draw(), add the following block — this checks to see if the value of rotateCount is above 359 (e.g. 360, a full circle). If so, it sets the value to its modulo of 360 (i.e. the remainder left over when the value is divided by 360) so the circle animation can continue uninterrupted, at a sensible, low value. Note that this isn't strictly necessary, but it is easier to work with values of 0–359 degrees than values like "128000 degrees".

    + +
    if (rotateCount > 359) {
    +  rotateCount %= 360;
    +}
    +
  16. +
  17. Next, below the previous block add the following line to actually rotate the spinner: +
    spinner.style.transform = `rotate(${rotateCount}deg)`;
    +
  18. +
  19. +

    At the very bottom inside the draw() function, insert the following line. This is the key to the whole operation — you are setting the variable defined earlier to an active requestAnimation() call, which takes the draw() function as its parameter. This starts the animation off, constantly running the draw() function at a rate as near 60 FPS as possible.

    + +
    rAF = requestAnimationFrame(draw);
    +
  20. +
+ +
+

Note: You can find this example live on GitHub. (You can see the source code, also.)

+
+ +

Clearing a requestAnimationFrame() call

+ +

Clearing a requestAnimationFrame() call can be done by calling the corresponding cancelAnimationFrame() method. (Note that the function name starts with "cancel", not "clear" as with the "set..." methods.) 

+ +

Just pass it the value returned by the requestAnimationFrame() call to cancel, which you stored in the variable rAF:

+ +
cancelAnimationFrame(rAF);
+ +

Active learning: Starting and stopping our spinner

+ +

In this exercise, we'd like you to test out the cancelAnimationFrame() method by taking our previous example and updating it, adding an event listener to start and stop the spinner when the mouse is clicked anywhere on the page.

+ +

Some hints:

+ + + +
+

Note: Try this yourself first; if you get really stuck, check out of our live example and source code.

+
+ +

Throttling a requestAnimationFrame() animation

+ +

One limitation of requestAnimationFrame() is that you can't choose your frame rate. This isn't a problem most of the time, as generally you want your animation to run as smoothly as possible. But what about when you want to create an old school, 8-bit-style animation?

+ +

This was a problem, for example, in the Monkey Island-inspired walking animation from our Drawing Graphics article:

+ +

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation.html", '100%', 260)}}

+ +

In this example, you have to animate both the position of the character on the screen, and the sprite being shown. There are only 6 frames in the sprite's animation. If you showed a different sprite frame for every frame displayed on the screen by requestAnimationFrame(), Guybrush would move his limbs too fast and the animation would look ridiculous. This example therefore throttles the rate at which the sprite cycles its frames using the following code:

+ +
if (posX % 13 === 0) {
+  if (sprite === 5) {
+    sprite = 0;
+  } else {
+    sprite++;
+  }
+}
+ +

So the code only cycles the sprite once every 13 animation frames.

+ +

...Actually, it's about every 6.5 frames, as we update posX (character's position on the screen) by two each frame:

+ +
if (posX > width/2) {
+  newStartPos = -( (width/2) + 102 );
+  posX = Math.ceil(newStartPos / 13) * 13;
+  console.log(posX);
+} else {
+  posX += 2;
+}
+ +

This is the code that calculates how to update the position in each animation frame.

+ +

The method you use to throttle your animation will depend on your particular code. For instance, in the earlier spinner example, you could make it appear to move slower by only increasing rotateCount by one on each frame, instead of two.

+ +

Active learning: a reaction game

+ +

For the final section of this article, you'll create a 2-player reaction game. The game will have two players, one of whom controls the game using the A key, and the other with the L key.

+ +

When the Start button is pressed, a spinner like the one we saw earlier is displayed for a random amount of time between 5 and 10 seconds. After that time, a message will appear saying "PLAYERS GO!!" — once this happens, the first player to press their control button will win the game.

+ +

{{EmbedGHLiveSample("learning-area/javascript/asynchronous/loops-and-intervals/reaction-game.html", '100%', 500)}}

+ +

Let's work through this:

+ +
    +
  1. +

    First of all, download the starter file for the app. This contains the finished HTML structure and CSS styling, giving us a game board that shows the two players' information (as seen above), but with the spinner and results paragraph displayed on top of one another. You just have to write the JavaScript code.

    +
  2. +
  3. +

    Inside the empty {{htmlelement("script")}} element on your page, start by adding the following lines of code that define some constants and variables you'll need in the rest of the code:

    + +
    const spinner = document.querySelector('.spinner p');
    +const spinnerContainer = document.querySelector('.spinner');
    +let rotateCount = 0;
    +let startTime = null;
    +let rAF;
    +const btn = document.querySelector('button');
    +const result = document.querySelector('.result');
    + +

    In order, these are:

    + +
      +
    1. A reference to the spinner, so you can animate it.
    2. +
    3. A reference to the {{htmlelement("div")}} element that contains the spinner, used for showing and hiding it.
    4. +
    5. A rotate count. This determines how much you want to show the spinner rotated on each frame of the animation.
    6. +
    7. A null start time. This will be populated with a start time when the spinner starts spinning.
    8. +
    9. An uninitialized variable to later store the {{domxref("Window.requestAnimationFrame", "requestAnimationFrame()")}} call that animates the spinner.
    10. +
    11. A reference to the Start button.
    12. +
    13. A reference to the results paragraph.
    14. +
    +
  4. +
  5. +

    Next, below the previous lines of code, add the following function. It simply takes two numbers and returns a random number between the two. You'll need this to generate a random timeout interval later on.

    + +
    function random(min,max) {
    +  var num = Math.floor(Math.random()*(max-min)) + min;
    +  return num;
    +}
    +
  6. +
  7. +

    Next add  the draw() function, which animates the spinner. This is very similar to the version from the simple spinner example, earlier:

    + +
    function draw(timestamp) {
    +  if(!startTime) {
    +   startTime = timestamp;
    +  }
    +
    +  rotateCount = (timestamp - startTime) / 3;
    +
    +  if(rotateCount > 359) {
    +    rotateCount %= 360;
    +  }
    +
    +  spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
    +  rAF = requestAnimationFrame(draw);
    +}
    +
  8. +
  9. +

    Now it is time to set up the initial state of the app when the page first loads. Add the following two lines, which simply hide the results paragraph and spinner container using display: none;.

    + +
    result.style.display = 'none';
    +spinnerContainer.style.display = 'none';
    +
  10. +
  11. +

    Next, define a reset() function, which sets the app back to the original state required to start the game again after it has been played. Add the following at the bottom of your code:

    + +
    function reset() {
    +  btn.style.display = 'block';
    +  result.textContent = '';
    +  result.style.display = 'none';
    +}
    +
  12. +
  13. +

    Okay, enough preparation!  It's time to make the game playable! Add the following block to your code. The start() function calls draw() to start the spinner spinning and display it in the UI, hides the Start button so you can't mess up the game by starting it multiple times concurrently, and runs a setTimeout() call that runs a setEndgame() function after a random interval between 5 and 10 seconds has passed. The following block also adds an event listener to your button to run the start() function when it is clicked.

    + +
    btn.addEventListener('click', start);
    +
    +function start() {
    +  draw();
    +  spinnerContainer.style.display = 'block';
    +  btn.style.display = 'none';
    +  setTimeout(setEndgame, random(5000,10000));
    +}
    + +
    +

    Note: You'll see this example is calling setTimeout() without storing the return value. (So, not let myTimeout = setTimeout(functionName, interval).) 

    + +

    This works just fine, as long as you don't need to clear your interval/timeout at any point. If you do, you'll need to save the returned identifier!

    +
    + +

    The net result of the previous code is that when the Start button is pressed, the spinner is shown and the players are made to wait a random amount of time before they are asked to press their button. This last part is handled by the setEndgame() function, which you'll define next.

    +
  14. +
  15. +

    Add the following function to your code next:

    + +
    function setEndgame() {
    +  cancelAnimationFrame(rAF);
    +  spinnerContainer.style.display = 'none';
    +  result.style.display = 'block';
    +  result.textContent = 'PLAYERS GO!!';
    +
    +  document.addEventListener('keydown', keyHandler);
    +
    +  function keyHandler(e) {
    +    console.log(e.key);
    +    if(e.key === 'a') {
    +      result.textContent = 'Player 1 won!!';
    +    } else if(e.key === 'l') {
    +      result.textContent = 'Player 2 won!!';
    +    }
    +
    +    document.removeEventListener('keydown', keyHandler);
    +    setTimeout(reset, 5000);
    +  };
    +}
    + +

    Stepping through this:

    + +
      +
    1. First, cancel the spinner animation with {{domxref("window.cancelAnimationFrame", "cancelAnimationFrame()")}} (it is always good to clean up unneeded processes), and hide the spinner container.
    2. +
    3. Next, display the results paragraph and set its text content to "PLAYERS GO!!" to signal to the players that they can now press their button to win.
    4. +
    5. Attach a keydown event listener to the document. When any button is pressed down, the keyHandler() function is run.
    6. +
    7. Inside keyHandler(), the code includes the event object as a parameter (represented by e) — its {{domxref("KeyboardEvent.key", "key")}} property contains the key that was just pressed, and you can use this to respond to specific key presses with specific actions.
    8. +
    9. Log e.key to the console, which is a useful way of finding out the key value of different keys you are pressing.
    10. +
    11. When e.key is "a", display a message to say that Player 1 won, and when e.key is "l", display a message to say Player 2 won. (Note: This will only work with lowercase a and l — if an uppercase A or L is submitted (the key plus Shift), it is counted as a different key!)
    12. +
    13. Regardless of which one of the player control keys was pressed,  remove the keydown event listener using {{domxref("EventTarget.removeEventListener", "removeEventListener()")}} so that once the winning press has happened, no more keyboard input is possible to mess up the final game result. You also use setTimeout() to call reset() after 5 seconds — as explained earlier, this function resets the game back to its original state so that a new game can be started.
    14. +
    +
  16. +
+ +

That's it—you're all done!

+ +
+

Note: If you get stuck, check out our version of the reaction game (see the source code also).

+
+ +

Conclusion

+ +

So that's it — all the essentials of async loops and intervals covered in one article. You'll find these methods useful in a lot of situations, but take care not to overuse them! Because they still run on the main thread, heavy and intensive callbacks (especially those that manipulate the DOM) can really slow down a page if you're not careful.

+ +

{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}

+ +

In this module

+ + diff --git a/files/pt-br/learn/javascript/asynchronous/async_await/index.html b/files/pt-br/learn/javascript/asynchronous/async_await/index.html deleted file mode 100644 index dd6a558cfe..0000000000 --- a/files/pt-br/learn/javascript/asynchronous/async_await/index.html +++ /dev/null @@ -1,556 +0,0 @@ ---- -title: Tornando mais fácil a programação assíncrona com async e await -slug: Learn/JavaScript/Asynchronous/Async_await -translation_of: Learn/JavaScript/Asynchronous/Async_await -original_slug: Learn/JavaScript/Asynchronous/Async_await ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Choosing_the_right_approach", "Learn/JavaScript/Asynchronous")}}
- -

Adições mais recentes à linguagem JavaScript são as funções assíncronas e a palavra-chave await, adicionadas no ECMAScript 2017. Esses recursos atuam basicamente como um syntactic sugar em cima de promises, tornando o código assíncrono mais fácil de escrever e ler. Isso faz com que o código assíncrono pareça mais com o código síncrono old-school, então vale a pena aprender. Este artigo fornece o que você precisa saber.

- - - - - - - - - - - - -
Pré-requisitos:Conhecimento básico de informática, uma compreensão razoável dos fundamentos do JavaScript, uma compreensão de código assíncrono em geral e promises.
Objetivo:Entender o uso do async/await.
- -

O básico de async/await

- -

Existem duas formas de usar async/await no seu código.

- -

A palavra-chave async

- -

Em primeiro lugar, temos a palavra-chave async, que você coloca antes de uma declaração de função para transformá-la em uma função assíncrona. Uma função assíncrona é uma função que espera a possibilidade de a palavra-chave await ser usada para invocar código assíncrono.

- -

Experimente digitar as seguintes linhas no console JS do seu navegador.

- -
function hello() { return "Hello" };
-    hello();
- -

A funcão retorna "Hello" — nada de especial, certo?

- -

Mas o que acontece se transformar-mos isso em uma função assíncrona? Tente o seguinte:

- -
async function hello() { return "Hello" };
-      hello();
- -

Ah. A invocação da função agora retorna uma promise. Isso é uma das características das funções assíncronas — seus valores de retorno são garantidos para serem convertidos em promises.

- -

Você também pode criar uma expressão de função assíncrona, assim:

- -
let hello = async function() { return "Hello" };
-      hello();
- -

E você pode usar arrow functions:

- -
let hello = async () => { return "Hello" };
- -

Tudo isso faz basicamente a mesma coisa.

- -

Para consumir o valor retornado quando a promise é finalizada, desde que esteja retornando uma promise, podemos usar um bloco .then():

- -
hello().then((value) => console.log(value))
- -

ou mesmo de forma simplificada

- -
hello().then(console.log)
- -

Como vimos no último artigo.

- -

Então a palavra-chave async é adicionada nas funções para dizer a elas para retornar uma promise ao invés de diretamente retornar uma valor.

-
- -

A palavra-chave await

- -
-

A vantagem de uma função assíncrona só se torna aparente quando você a combina com a palavra-chave await. await só funciona dentro de funções assíncronas no código JavaScript regular, no entanto, pode ser usado por conta própria com JavaScript modules.

- -

await pode ser colocado na frente de qualquer função assíncrona baseada em promise para pausar seu código nessa linha até que a promise seja resolvida e, em seguida, retornar o valor resultante.

- -

Você pode usar await quando chamar qualquer função que retorne uma Promise, incluindo funções de API web.

- -

Aqui está um exemplo comum:

- -
async function hello() {
-      return greeting = await Promise.resolve("Hello");
-    };
-    
-    hello().then(alert);
- -

Com certeza, o exemplo acima não é muito útil, porém serve para ilustrar a sintaxe. Vamos seguir em frente e ver um exemplo real.

-
- -

Reescrevendo código baseado em promises com async/await

- -
-

Vejamos um exemplo simples de busca que vimos no artigo anterior:

- -
fetch('coffee.jpg')
-    .then(response => {
-      if (!response.ok) {
-        throw new Error(`HTTP error! status: ${response.status}`);
-      }
-      return response.blob();
-    })
-    .then(myBlob => {
-      let objectURL = URL.createObjectURL(myBlob);
-      let image = document.createElement('img');
-      image.src = objectURL;
-      document.body.appendChild(image);
-    })
-    .catch(e => {
-      console.log('There has been a problem with your fetch operation: ' + e.message);
-    });
- -

Por enquanto, você precisa ter um entendimento razoável das promises e como elas funcionam, mas vamos converter isso para usar async/await e ver o quão simples as coisas se tornam:

- -
async function myFetch() {
-      let response = await fetch('coffee.jpg');
-    
-      if (!response.ok) {
-        throw new Error(`HTTP error! status: ${response.status}`);
-      }
-    
-      let myBlob = await response.blob();
-    
-      let objectURL = URL.createObjectURL(myBlob);
-      let image = document.createElement('img');
-      image.src = objectURL;
-      document.body.appendChild(image);
-    }
-    
-    myFetch()
-    .catch(e => {
-      console.log('There has been a problem with your fetch operation: ' + e.message);
-    });
- -

Isto faz o código muito mais simples and fácil de entender — sem mais blocos .then() em todo lugar!

- -

Visto que a palavra-chave async transforma a funcão em uma promise, você pode refatorar seu código para usar uma abordagem de promises e await, trazendo a segunda metade da funcão para um novo bloco e torná-la mais flexível:

- -
async function myFetch() {
-      let response = await fetch('coffee.jpg');
-      if (!response.ok) {
-        throw new Error(`HTTP error! status: ${response.status}`);
-      }
-      return await response.blob();
-    
-    }
-    
-    myFetch().then((blob) => {
-      let objectURL = URL.createObjectURL(blob);
-      let image = document.createElement('img');
-      image.src = objectURL;
-      document.body.appendChild(image);
-    }).catch(e => console.log(e));
- -

Você pode tentar fazer o exemplo sozinho, ou rodar o nosso exemplo ao vivo (veja também ocódigo-fonte).

-
- -

Mas como isso funciona?

- -
-

Você notará que empacotamos o código dentro de uma função, e incluímos a palavra-chave async antes da palavra-chavefunction. Isso é necessário — você tem que criar uma função assíncrona para definir o bloco de código no qual você executará seu código assíncrono; como falamos mais cedo, await só funciona dentro de funções assíncronas.

- -

Dentro da definição da função myFetch() você pode ver que o código se parece muito à versão anterior com promise, , mas tem algumas diferenças. Ao invés de precisar encadear um bloco .then() no final de cada método baseado em promise, você apenas adiciona a palavra-chave await antes de cada chamada de método, e então atribui o resultado a variável. A palavra-chave await faz com que o JavaScript pause seu código em tempo de execução nesta linha, não permitindo mais  nenhum código ser executado nesse meio tempo até que a chamada de função assíncrona retorne seu resultado — muito útil se o código subsequente depender desse resultado!

- -

Assim que estiver completo, seu código continua a ser executado começando na próxima linha. Por exemplo:

- -
let response = await fetch('coffee.jpg');
- -

A resposta retornada pela promise fetch() preenchida é atribuída a variável response quando a resposta estiver disponível, e o parser pausa nesta linha até que isso ocorra. Uma vez que a resposta está disponível, o parser move para a próxima linha, o qual cria o Blob fora dele. Esta linha também invoca um método assíncrono baseado em promise, assim podemos usar await aqui também. Quando o resultado da operação retorna, retornamos isso fora da funcão myFetch().

- -

Isso significa que quando nós chamamos a função myFetch(), isso retorna uma promise, então podemos encadear um .then() no final, dentro do qual lidamos com a exibição do blob na tela.

- -

Provavelmente você já está pensando "isso é realmente demais!", e você está certo — menos blocos .then() para envolver o código, e quase sempre se parece com um código síncrono, por isso é muito intuitivo.

-
- -

Adicionando tratamento de erros

- -
-

E se você deseja adicionar tratamento de erros, você tem algumas opções.

- -

Você pode usar uma estrutura try...catch síncrona com async/await. Este exemplo se expande da primeira versão do código que mostramos acima:

- -
async function myFetch() {
-      try {
-        let response = await fetch('coffee.jpg');
-    
-        if (!response.ok) {
-          throw new Error(`HTTP error! status: ${response.status}`);
-        }
-        let myBlob = await response.blob();
-        let objectURL = URL.createObjectURL(myBlob);
-        let image = document.createElement('img');
-        image.src = objectURL;
-        document.body.appendChild(image);
-    
-      } catch(e) {
-        console.log(e);
-      }
-    }
-    
-    myFetch();
- -

Ao bloco catch() {} é passado um objeto de erro, qual nós chamamos e; agora podemos registrar isso no console, e isso nos fornecerá uma mensagem de erro detalhada mostrando onde o erro foi gerado no código.

- -

Se você quiser usar a segunda versão (refatorada) do código que mostramos acima, seria melhor apenas continuar a abordagem híbrida e encadear um bloco .catch() no final da chamada .then(), dessa forma:

- -
async function myFetch() {
-      let response = await fetch('coffee.jpg');
-      if (!response.ok) {
-        throw new Error(`HTTP error! status: ${response.status}`);
-      }
-      return await response.blob();
-    
-    }
-    
-    myFetch().then((blob) => {
-      let objectURL = URL.createObjectURL(blob);
-      let image = document.createElement('img');
-      image.src = objectURL;
-      document.body.appendChild(image);
-    })
-    .catch((e) =>
-      console.log(e)
-    );
- -

Isso ocorre porque o bloco .catch() vai pegar os erros que ocorrem em ambos, na chamada de função com async e com cadeia de promises. Se você usou bloco try/catch aqui, você ainda pode obter erros não tratados na função myFetch() quando ela for chamada.

- -

Você pode encontrar esses dois exemplos no GitHub:

- - -
- -

Esperando um Promise.all()

- -
-

async/await é construído em cima de promises, por isso é compatível com todos os recursos oferecidos por promises. Isso inclui Promise.all() — você pode esperar felizmente uma chamada Promise.all() para obter todos os resultados retornados em uma variável de uma forma que se pareça com um código síncrono simples. De novo, vamos voltar para um exemplo que vimos em nosso artigo anterior. Mantenha-o aberto em uma guia separada para que você possa comparar e contrastar com a nova versão mostrada abaixo.

- -

Convertendo este para async/await (veja demonstração ao vivo e código-fonte), isso agora parece assim:

- -
async function fetchAndDecode(url, type) {
-      let response = await fetch(url);
-    
-      let content;
-    
-      if (!response.ok) {
-        throw new Error(`HTTP error! status: ${response.status}`);
-      } else {
-        if(type === 'blob') {
-          content = await response.blob();
-        } else if(type === 'text') {
-          content = await response.text();
-        }
-      }
-    
-      return content;
-    
-    
-    }
-    
-    async function displayContent() {
-      let coffee = fetchAndDecode('coffee.jpg', 'blob');
-      let tea = fetchAndDecode('tea.jpg', 'blob');
-      let description = fetchAndDecode('description.txt', 'text');
-    
-      let values = await Promise.all([coffee, tea, description]);
-    
-      let objectURL1 = URL.createObjectURL(values[0]);
-      let objectURL2 = URL.createObjectURL(values[1]);
-      let descText = values[2];
-    
-      let image1 = document.createElement('img');
-      let image2 = document.createElement('img');
-      image1.src = objectURL1;
-      image2.src = objectURL2;
-      document.body.appendChild(image1);
-      document.body.appendChild(image2);
-    
-      let para = document.createElement('p');
-      para.textContent = descText;
-      document.body.appendChild(para);
-    }
-    
-    displayContent()
-    .catch((e) =>
-      console.log(e)
-    );
- -

Você notará que a função fetchAndDecode() foi convertida facilmente em uma função assíncrona com apenas algumas alterações. Veja a linha do Promise.all():

- -
let values = await Promise.all([coffee, tea, description]);
- -

Usando await aqui podemos obter todos os resultados das três promises retornadas no array values, quando todos eles estão disponíveis, de uma forma que se parece muito com o código síncrono. Tivemos que envolver todo o código em uma nova função assíncrona, displayContent(), e não reduzimos o código em muitas linhas, mas ser capaz de mover a maior parte do código para fora do bloco .then() fornece uma simplificação agradável e útil, deixando-nos com um programa muito mais legível.

- -

Para tratamento de erros, nós incluímos um bloco .catch() no nossa chamada displayContent(); isso vai lidar com os erros que ocorrem em ambas as funções.

- -
-

Nota: Também é possível usar um bloco finally síncrono na função assíncrona, no lugar de um bloco assíncrono.finally(), para mostrar um relatório final sobre como foi a operação — você pode ver isso em ação no nosso exemplo ao vivo (veja também o código-fonte).

-
-
- -

Tratando lentidão com async/await

- -
-

Async/await faz seu código parecer síncrono e, de certa forma, faz com que se comporte de maneira mais síncrona. A palavra-chave await bloqueia a execução de todo o código que o segue até que a promise seja cumprida, exatamente como faria com uma operação síncrona. Ele permite que outras tarefas continuem sendo executadas enquanto isso, mas o código com await é bloqueado. Por exemplo:

- -
async function makeResult(items) {
-      let newArr = [];
-      for(let i=0; i < items.length; i++) {
-        newArr.push('word_'+i);
-      }
-      return newArr;
-    }
-    
-    async function getResult() {
-      let result = await makeResult(items); // Blocked on this line
-      useThatResult(result); // Will not be executed before makeResult() is done
-    }
-    
-    
- -

Como resultado, seu código pode ser retardado por um número significativo de promises aguardadas acontecendo uma após a outra. Cada await vai esperar que o anterior termine, ao passo que, na verdade, o que você pode querer é que as promises comecem a ser processadas simultaneamente, como fariam se não estivéssemos usando async/await.

- -

Vejamos esses dois exemplos — slow-async-await.html (veja código-fonte) e fast-async-await.html (veja código-fonte). Ambos começam com uma função promise personalizada que simula um processo assíncrono com uma chamada setTimeout():

- -
function timeoutPromise(interval) {
-      return new Promise((resolve, reject) => {
-        setTimeout(function(){
-          resolve("done");
-        }, interval);
-      });
-    };
- -

Cada um deles inclui uma função assíncrona timeTest() que espera três chamadas timeoutPromise():

- -
async function timeTest() {
-      ...
-    }
- -

Cada um termina registrando um horário de início, vendo quanto tempo a promise timeTest() leva para completar, em seguida, registrando um horário de término e relatando quanto tempo a operação levou no total:

- -
let startTime = Date.now();
-    timeTest().then(() => {
-      let finishTime = Date.now();
-      let timeTaken = finishTime - startTime;
-      alert("Time taken in milliseconds: " + timeTaken);
-    })
- -

Isso é a função timeTest() que difere em cada caso.

- -

No exemplo slow-async-await.html, timeTest() se parece com isso:

- -
async function timeTest() {
-      await timeoutPromise(3000);
-      await timeoutPromise(3000);
-      await timeoutPromise(3000);
-    }
- -

Aqui esperamos diretamente todas as três chamadas timeoutPromise(), fazendo cada uma a cada 3 segundos. Cada chamada subsequente é forçada a esperar até que a última termine — se você executar o primeiro exemplo, você verá a caixa de alerta relatando um tempo total de execução de cerca de 9 segundos.

- -

No exemplo fast-async-await.html, timeTest() se parece com isso:

- -
async function timeTest() {
-      const timeoutPromise1 = timeoutPromise(3000);
-      const timeoutPromise2 = timeoutPromise(3000);
-      const timeoutPromise3 = timeoutPromise(3000);
-    
-      await timeoutPromise1;
-      await timeoutPromise2;
-      await timeoutPromise3;
-    }
- -

Aqui nós armazenamos os três objetos Promise em variáveis, que tem o efeito de desencadear seus processos associados, todos rodando simultaneamente.

- -

A seguir, aguardamos seus resultados — porque todas as promises começaram a ser processadas essencialmente ao mesmo tempo, as promises serão cumpridas todas ao mesmo tempo; ao executar o segundo exemplo, você verá a caixa de alerta relatando um tempo total de execução de pouco mais de 3 segundos!

-
- -

Tratamento de erros

- -
-

Há um problema com o padrão acima, no entanto — pode levar a erros não tratados.

- -

Vamos atualizar os exemplos anteriores, desta vez adicionando uma promise rejeitada e uma declaração catch no final:

- -
function timeoutPromiseResolve(interval) {
-      return new Promise((resolve, reject) => {
-        setTimeout(function(){
-          resolve("successful");
-        }, interval);
-      });
-    };
-    
-    function timeoutPromiseReject(interval) {
-      return new Promise((resolve, reject) => {
-        setTimeout(function(){
-          reject("error");
-        }, interval);
-      });
-    };
-    
-    async function timeTest() {
-      await timeoutPromiseResolve(5000);
-      await timeoutPromiseReject(2000);
-      await timeoutPromiseResolve(3000);
-    }
-    
-    let startTime = Date.now();
-    timeTest().then(() => {})
-    .catch(e => {
-      console.log(e);
-      let finishTime = Date.now();
-      let timeTaken = finishTime - startTime;
-      alert("Time taken in milliseconds: " + timeTaken);
-    })
- -

No exemplo acima, o erro é tratado corretamente, e o alerta aparece após aproximadamente 7 segundos.

- -

Agora no segundo padrão:

- -
function timeoutPromiseResolve(interval) {
-      return new Promise((resolve, reject) => {
-        setTimeout(function(){
-          resolve("successful");
-        }, interval);
-      });
-    };
-    
-    function timeoutPromiseReject(interval) {
-      return new Promise((resolve, reject) => {
-        setTimeout(function(){
-          reject("error");
-        }, interval);
-      });
-    };
-    
-    async function timeTest() {
-      const timeoutPromiseResolve1 = timeoutPromiseResolve(5000);
-      const timeoutPromiseReject2 = timeoutPromiseReject(2000);
-      const timeoutPromiseResolve3 = timeoutPromiseResolve(3000);
-    
-      await timeoutPromiseResolve1;
-      await timeoutPromiseReject2;
-      await timeoutPromiseResolve3;
-    }
-    
-    let startTime = Date.now();
-    timeTest().then(() => {
-    }).catch(e => {
-      console.log(e);
-      let finishTime = Date.now();
-      let timeTaken = finishTime - startTime;
-      alert("Time taken in milliseconds: " + timeTaken);
-    })
- -

Neste exemplo, temos um erro não tratado no console (depois de 2 segundos), e o alerta aparece após aproximadamente 5 segundos.

- -

Para iniciar as promises em paralelo e detectar o erro corretamente, nós poderíamos usar Promise.all(), como discutido anteriormente:

- -
function timeoutPromiseResolve(interval) {
-      return new Promise((resolve, reject) => {
-        setTimeout(function(){
-          resolve("successful");
-        }, interval);
-      });
-    };
-    
-    function timeoutPromiseReject(interval) {
-      return new Promise((resolve, reject) => {
-        setTimeout(function(){
-          reject("error");
-        }, interval);
-      });
-    };
-    
-    async function timeTest() {
-      const timeoutPromiseResolve1 = timeoutPromiseResolve(5000);
-      const timeoutPromiseReject2 = timeoutPromiseReject(2000);
-      const timeoutPromiseResolve3 = timeoutPromiseResolve(3000);
-    
-      const results = await Promise.all([timeoutPromiseResolve1, timeoutPromiseReject2, timeoutPromiseResolve3]);
-      return results;
-    }
-    
-    let startTime = Date.now();
-    timeTest().then(() => {
-    }).catch(e => {
-      console.log(e);
-      let finishTime = Date.now();
-      let timeTaken = finishTime - startTime;
-      alert("Time taken in milliseconds: " + timeTaken);
-    })
- -

Neste exemplo, o erro é tratado corretamente após aproximadamente 2 segundos e também vemos o alerta após aproximadamente 2 segundos.

- -

A Promise.all() rejeita quando qualquer uma das promises de entrada é rejeitada. Se você deseja que todas as promises sejam cumpridas e, em seguida, usar alguns de seus valores retornados, mesmo quando alguns deles são rejeitados, você pode usar Promise.allSettled().

-
- -

Async/await em métodos de classe

- -
-

Como nota final, antes de prosseguirmos, você pode até adicionar async na frente de métodos de classe / objeto para fazê-los retornar promises, e await promises dentro deles. Dê uma olhada no artigo Código de classe ES que vimos em nosso JavaScript orientado a objetos. em seguida, olhe para nossa versão modificada com um método async:

- -
class Person {
-      constructor(first, last, age, gender, interests) {
-        this.name = {
-          first,
-          last
-        };
-        this.age = age;
-        this.gender = gender;
-        this.interests = interests;
-      }
-    
-      async greeting() {
-        return await Promise.resolve(`Hi! I'm ${this.name.first}`);
-      };
-    
-      farewell() {
-        console.log(`${this.name.first} has left the building. Bye for now!`);
-      };
-    }
-    
-    let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
- -

O primeiro método da classe agora pode ser usado assim:

- -
han.greeting().then(console.log);
-
- -

Suporte de navegador

- -
-

Uma consideração ao decidir se deve usar async/await é o suporte para navegadores mais antigos. Eles estão disponíveis em versões modernas da maioria dos navegadores, o mesmo que promises; os principais problemas de suporte vêm com o Internet Explorer e o Opera Mini.

- -

Se você deseja usar async/await, mas está preocupado com o suporte a navegadores mais antigos, pode considerar o uso da biblioteca BabelJS — isso permite que você escreva seus aplicativos usando o JavaScript mais recente e deixe Babel descobrir quais mudanças, se houver, são necessárias para os navegadores de seu usuário. Ao encontrar um navegador que não suporta async/await, o polyfill do Babel pode fornecer automaticamente substitutos que funcionam em navegadores mais antigos.

-
- -

Conclusão

- -

E aí está - async/await fornecem uma maneira agradável e simplificada de escrever código assíncrono que é mais simples de ler e manter. Mesmo com o suporte do navegador sendo mais limitado do que outros mecanismos de código assíncrono no momento da escrita, vale a pena aprender e considerar o uso, agora e no futuro.

- -

{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Choosing_the_right_approach", "Learn/JavaScript/Asynchronous")}}

- -

Neste módulo

- - - \ No newline at end of file diff --git a/files/pt-br/learn/javascript/asynchronous/choosing_the_right_approach/index.html b/files/pt-br/learn/javascript/asynchronous/choosing_the_right_approach/index.html deleted file mode 100644 index e168a76aef..0000000000 --- a/files/pt-br/learn/javascript/asynchronous/choosing_the_right_approach/index.html +++ /dev/null @@ -1,524 +0,0 @@ ---- -title: Escolhendo a abordagem correta -slug: Learn/JavaScript/Asynchronous/Choosing_the_right_approach -translation_of: Learn/JavaScript/Asynchronous/Choosing_the_right_approach -original_slug: Learn/JavaScript/Asynchronous/Escolhendo_abordagem_correta ---- -
{{LearnSidebar}}
- -
{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}
- -

To finish this module off, we'll provide a brief discussion of the different coding techniques and features we've discussed throughout, looking at which one you should use when, with recommendations and reminders of common pitfalls where appropriate. We'll probably add to this resource as time goes on.

- - - - - - - - - - - - -
Prerequisites:Basic computer literacy, a reasonable understanding of JavaScript fundamentals.
Objective:To be able to make a sound choice of when to use different asynchronous programming techniques.
- -

Asynchronous callbacks

- -

Generally found in old-style APIs, involves a function being passed into another function as a parameter, which is then invoked when an asynchronous operation has been completed, so that the callback can in turn do something with the result. This is the precursor to promises; it's not as efficient or flexible. Use only when necessary.

- - - - - - - - - - - - - - - - - - - -
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoYes (recursive callbacks)Yes (nested callbacks)No
- -

Code example

- -

An example that loads a resource via the XMLHttpRequest API (run it live, and see the source):

- -
function loadAsset(url, type, callback) {
-  let xhr = new XMLHttpRequest();
-  xhr.open('GET', url);
-  xhr.responseType = type;
-
-  xhr.onload = function() {
-    callback(xhr.response);
-  };
-
-  xhr.send();
-}
-
-function displayImage(blob) {
-  let objectURL = URL.createObjectURL(blob);
-
-  let image = document.createElement('img');
-  image.src = objectURL;
-  document.body.appendChild(image);
-}
-
-loadAsset('coffee.jpg', 'blob', displayImage);
- -

Pitfalls

- - - -

Browser compatibility

- -

Really good general support, although the exact support for callbacks in APIs depends on the particular API. Refer to the reference documentation for the API you're using for more specific support info.

- -

Further information

- - - -

setTimeout()

- -

setTimeout() is a method that allows you to run a function after an arbitrary amount of time has passed.

- - - - - - - - - - - - - - - - - -
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
YesYes (recursive timeouts)Yes (nested timeouts)No
- -

Code example

- -

Here the browser will wait two seconds before executing the anonymous function, then will display the alert message (see it running live, and see the source code):

- -
let myGreeting = setTimeout(function() {
-  alert('Hello, Mr. Universe!');
-}, 2000)
- -

Pitfalls

- -

You can use recursive setTimeout() calls to run a function repeatedly in a similar fashion to setInterval(), using code like this:

- -
let i = 1;
-setTimeout(function run() {
-  console.log(i);
-  i++;
-
-  setTimeout(run, 100);
-}, 100);
- -

There is a difference between recursive setTimeout() and setInterval():

- - - -

When your code has the potential to take longer to run than the time interval you’ve assigned, it’s better to use recursive setTimeout() — this will keep the time interval constant between executions regardless of how long the code takes to execute, and you won't get errors.

- -

Browser compatibility

- -

{{Compat("api.WindowOrWorkerGlobalScope.setTimeout")}}

- -

Further information

- - - -

setInterval()

- -

setInterval() is a method that allows you to run a function repeatedly with a set interval of time between each execution. Not as efficient as requestAnimationFrame(), but allows you to choose a running rate/frame rate.

- - - - - - - - - - - - - - - - - -
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoYesNo (unless it is the same one)No
- -

Code example

- -

The following function creates a new Date() object, extracts a time string out of it using toLocaleTimeString(), and then displays it in the UI. We then run it once per second using setInterval(), creating the effect of a digital clock that updates once per second (see this live, and also see the source):

- -
function displayTime() {
-   let date = new Date();
-   let time = date.toLocaleTimeString();
-   document.getElementById('demo').textContent = time;
-}
-
-const createClock = setInterval(displayTime, 1000);
- -

Pitfalls

- - - -

Browser compatibility

- -

{{Compat("api.WindowOrWorkerGlobalScope.setInterval")}}

- -

Further information

- - - -

requestAnimationFrame()

- -

requestAnimationFrame() is a method that allows you to run a function repeatedly, and efficiently, at the best framerate available given the current browser/system. You should, if at all possible, use this instead of setInterval()/recursive setTimeout(), unless you need a specific framerate.

- - - - - - - - - - - - - - - - - -
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoYesNo (unless it is the same one)No
- -

Code example

- -

A simple animated spinner; you can find this example live on GitHub (see the source code also):

- -
const spinner = document.querySelector('div');
-let rotateCount = 0;
-let startTime = null;
-let rAF;
-
-function draw(timestamp) {
-    if(!startTime) {
-        startTime = timestamp;
-    }
-
-    rotateCount = (timestamp - startTime) / 3;
-
-    if(rotateCount > 359) {
-        rotateCount %= 360;
-    }
-
-    spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
-
-    rAF = requestAnimationFrame(draw);
-}
-
-draw();
- -

Pitfalls

- - - -

Browser compatibility

- -

{{Compat("api.Window.requestAnimationFrame")}}

- -

Further information

- - - -

Promises

- -

Promises are a JavaScript feature that allows you to run asynchronous operations and wait until it is definitely complete before running another operation based on its result. Promises are the backbone of modern asynchronous JavaScript.

- - - - - - - - - - - - - - - - - -
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoNoYesSee Promise.all(), below
- -

Code example

- -

The following code fetches an image from the server and displays it inside an {{htmlelement("img")}} element; see it live also, and see also the source code:

- -
fetch('coffee.jpg')
-.then(response => response.blob())
-.then(myBlob => {
-  let objectURL = URL.createObjectURL(myBlob);
-  let image = document.createElement('img');
-  image.src = objectURL;
-  document.body.appendChild(image);
-})
-.catch(e => {
-  console.log('There has been a problem with your fetch operation: ' + e.message);
-});
- -

Pitfalls

- -

Promise chains can be complex and hard to parse. If you nest a number of promises, you can end up with similar troubles to callback hell. For example:

- -
remotedb.allDocs({
-  include_docs: true,
-  attachments: true
-}).then(function (result) {
-  let docs = result.rows;
-  docs.forEach(function(element) {
-    localdb.put(element.doc).then(function(response) {
-      alert("Pulled doc with id " + element.doc._id + " and added to local db.");
-    }).catch(function (err) {
-      if (err.name == 'conflict') {
-        localdb.get(element.doc._id).then(function (resp) {
-          localdb.remove(resp._id, resp._rev).then(function (resp) {
-// et cetera...
- -

It is better to use the chaining power of promises to go with a flatter, easier to parse structure:

- -
remotedb.allDocs(...).then(function (resultOfAllDocs) {
-  return localdb.put(...);
-}).then(function (resultOfPut) {
-  return localdb.get(...);
-}).then(function (resultOfGet) {
-  return localdb.put(...);
-}).catch(function (err) {
-  console.log(err);
-});
- -

or even:

- -
remotedb.allDocs(...)
-.then(resultOfAllDocs => {
-  return localdb.put(...);
-})
-.then(resultOfPut => {
-  return localdb.get(...);
-})
-.then(resultOfGet => {
-  return localdb.put(...);
-})
-.catch(err => console.log(err));
- -

That covers a lot of the basics. For a much more complete treatment, see the excellent We have a problem with promises, by Nolan Lawson.

- -

Browser compatibility

- -

{{Compat("javascript.builtins.Promise")}}

- -

Further information

- - - -

Promise.all()

- -

A JavaScript feature that allows you to wait for multiple promises to complete before then running a further operation based on the results of all the other promises.

- - - - - - - - - - - - - - - - - -
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoNoNoYes
- -

Code example

- -

The following example fetches several resources from the server, and uses Promise.all() to wait for all of them to be available before then displaying all of them — see it live, and see the source code:

- -
function fetchAndDecode(url, type) {
-  // Returning the top level promise, so the result of the entire chain is returned out of the function
-  return fetch(url).then(response => {
-    // Depending on what type of file is being fetched, use the relevant function to decode its contents
-    if(type === 'blob') {
-      return response.blob();
-    } else if(type === 'text') {
-      return response.text();
-    }
-  })
-  .catch(e => {
-    console.log(`There has been a problem with your fetch operation for resource "${url}": ` + e.message);
-  });
-}
-
-// Call the fetchAndDecode() method to fetch the images and the text, and store their promises in variables
-let coffee = fetchAndDecode('coffee.jpg', 'blob');
-let tea = fetchAndDecode('tea.jpg', 'blob');
-let description = fetchAndDecode('description.txt', 'text');
-
-// Use Promise.all() to run code only when all three function calls have resolved
-Promise.all([coffee, tea, description]).then(values => {
-  console.log(values);
-  // Store each value returned from the promises in separate variables; create object URLs from the blobs
-  let objectURL1 = URL.createObjectURL(values[0]);
-  let objectURL2 = URL.createObjectURL(values[1]);
-  let descText = values[2];
-
-  // Display the images in <img> elements
-  let image1 = document.createElement('img');
-  let image2 = document.createElement('img');
-  image1.src = objectURL1;
-  image2.src = objectURL2;
-  document.body.appendChild(image1);
-  document.body.appendChild(image2);
-
-  // Display the text in a paragraph
-  let para = document.createElement('p');
-  para.textContent = descText;
-  document.body.appendChild(para);
-});
- -

Pitfalls

- - - -

Browser compatibility

- -

{{Compat("javascript.builtins.Promise.all")}}

- -

Further information

- - - -

Async/await

- -

Syntactic sugar built on top of promises that allows you to run asynchronous operations using syntax that's more like writing synchronous callback code.

- - - - - - - - - - - - - - - - - -
Useful for...
Single delayed operationRepeating operationMultiple sequential operationsMultiple simultaneous operations
NoNoYesYes (in combination with Promise.all())
- -

Code example

- -

The following example is a refactor of the simple promise example we saw earlier that fetches and displays an image, written using async/await (see it live, and see the source code):

- -
async function myFetch() {
-  let response = await fetch('coffee.jpg');
-  let myBlob = await response.blob();
-
-  let objectURL = URL.createObjectURL(myBlob);
-  let image = document.createElement('img');
-  image.src = objectURL;
-  document.body.appendChild(image);
-}
-
-myFetch();
- -

Pitfalls

- - - -

Browser compatibility

- -

{{Compat("javascript.statements.async_function")}}

- -

Further information

- - - -

{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}

- -

In this module

- - diff --git a/files/pt-br/learn/javascript/asynchronous/concepts/index.html b/files/pt-br/learn/javascript/asynchronous/concepts/index.html deleted file mode 100644 index 39ce5d5086..0000000000 --- a/files/pt-br/learn/javascript/asynchronous/concepts/index.html +++ /dev/null @@ -1,156 +0,0 @@ ---- -title: Conceitos gerais da programação assíncrona -slug: Learn/JavaScript/Asynchronous/Concepts -translation_of: Learn/JavaScript/Asynchronous/Concepts -original_slug: Learn/JavaScript/Asynchronous/Conceitos ---- -
{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}}
- -

Neste artigo, nós vamos ver um número de conceitos importantes relativos à programação assíncrona e como ela se parece em navegadores modernos e em JavaScript. Você deve entender estes conceitos antes de trabalhar com outros artigos neste módulo.

- - - - - - - - - - - - -
Pré-requisitos:Conhecimentos básicos de informática e compreensão dos fundamentos de JavaScript.
Objetivo:Entender os conceitos básicos da programação assíncrona e como ela se manifesta em navegadores e JavaScript.
- -

Assíncrono?

- -

Normalmente, o código de um programa é executado de forma direta, com uma coisa acontecendo por vez. Se uma função depende do resultado de outra função, ela tem que esperar o retorno do resultado, e até que isso aconteça, o programa inteiro praticamente para de funcionar da perspectiva do usuário.

- -

Usuários do Mac, por exemplo, conseguem ver isso como o cursor giratório em arco-íris (ou "beachball", como normalmente é chamado). Este cursor é o jeito do sistema operacional dizer: "o programa atual que você está usando teve que parar e esperar algo terminar de ser executado, e estava demorando tanto que fiquei preocupado se você estava pensando no que aconteceu."

- -

Multi-colored macOS beachball busy spinner

- -

Essa é uma situação frustrante, e não faz bom uso do poder de processamento do computador — especialmente em uma era em que computadores tem múltiplos núcleos de processamento disponíveis. Não há sentido em ficar esperando por algo quando você pode deixar outra tarefa ser executada em um núcleo de processador diferente e deixar que ele te avise quando terminar. Isso te permite fazer mais coisas por enquanto, o que é a base da programação assincrona. Depende do ambiente de programação que você está usando (navegadores da Web, no caso de desenvolvimento da Web) para fornecer APIs que permitem executar essas tarefas de forma assíncrona.

- -

Bloqueio de código

- -

Técnicas async (assíncronas) são muito úteis, principalmente na programação web. Quando um aplicativo web é executado em um navegador e executa um pedaço de código rigoroso sem retornar o controle para o navegador, ele pode parecer que travou. Isso é chamado de blocking; o navegador está bloqueado de continuar a manusear a entrada do usuário e de realizar outras tarefas até que o aplicativo web retorne o controle do processador.

- -

Vamos dar uma olhadinha em alguns exemplos para que você entenda o blocking.

- -

No nosso exemplo simple-sync.html (veja aqui), nós adicionamos um evento de click em um botão para que, quando clicado, ele executa uma tarefa pesada (calcula 10 milhões de datas e depois imprime a última delas no console) e depois adiciona um parágrafo no DOM:

- -
const btn = document.querySelector('button');
-btn.addEventListener('click', () => {
-  let myDate;
-  for(let i = 0; i < 10000000; i++) {
-    let date = new Date();
-    myDate = date
-  }
-
-  console.log(myDate);
-
-  let pElem = document.createElement('p');
-  pElem.textContent = 'This is a newly-added paragraph.';
-  document.body.appendChild(pElem);
-});
- -

Quando o exemplo for executado, abra seu console JavaScript e depois clique no botão  — você verá qua o parágrafo não aparece até que o programa termine de calcular as datas e imprimir a última no console. O código é executado na ordem em que ele aparece na fonte, e a operação seguinte só é executada depois que a primeira for terminada.

- -
-

Nota: O exemplo anterior não é muito realistico. Você nunca calcularia 10 milhões de datas em um aplicativo real! Mas isso serve par te dar um apoio sobre o assunto.

-
- -

No nosso segundo exemplo simple-sync-ui-blocking.html (veja aqui), nós simulamos algo mais realistico que você pode encontrar em uma página real. Nós bloqueamos a interatividade do usuário na renderização da UI. Neste exemplo, nós temos dois botões:

- - - -
function expensiveOperation() {
-  for(let i = 0; i < 1000000; i++) {
-    ctx.fillStyle = 'rgba(0,0,255, 0.2)';
-    ctx.beginPath();
-    ctx.arc(random(0, canvas.width), random(0, canvas.height), 10, degToRad(0), degToRad(360), false);
-    ctx.fill()
-  }
-}
-
-fillBtn.addEventListener('click', expensiveOperation);
-
-alertBtn.addEventListener('click', () =>
-  alert('You clicked me!')
-);
- -

Se você clicar no primeiro botão e imediatamente no segundo, você verá que a mensagem de alerta não aparece até que os círculos sejam totalmente renderizados. A primeira operação bloqueia a segunda até a sua finalização.

- -
-

Nota: OK, no nosso caso, isso é ruim e estamos bloqueando o código de propósito, mas isso é um problema comum que desenvolvedores de aplicativos reais sempre tentam resolver.

-
- -

E por quê isso acontece? A resposta é que o JavaScript é single threaded. E é neste ponto que precisamos introduzir a você o conceito de threads.

- -

Threads

- -

Uma thread é basicamente um único processo que um programa pode usar para concluir tarefas. Cada thread só pode fazer uma tarefa de cada vez:

- -
Tarefa A --> Tarefa B --> Tarefa C
- -

Cada tarefa será executada sequencialmente; uma tarefa tem que ser concluída antes que a próxima possa ser iniciada.

- -

Como foi dito anteriormente, muitos computadores possuem múltiplos núcleos, para que possam fazer múltiplas coisas de uma vez só. Linguagens de programação que podem suportar múltiplas threads podem usar múltiplos processadores para concluir múltiplas tarefas simultâneamente:

- -
Thread 1: Tarefa A --> Tarefa B
-Thread 2: Tarefa C --> Tarefa D
- -

JavaScript é single threaded

- -

JavaScript é tradicionalmente single-threaded. Mesmo com múltiplos núcleos de processamento, você só pode fazê-lo executar tarefas em uma única thread, chamada de main thread (thread principal). Nosso exemplo de cima é executado assim:

- -
Main thread: Renderizar circulos no canvas --> Mostrar alert()
- -

Depois de um tempo, o JavaScript ganhou algumas ferramentas para ajudar em tais problemas. As Web workers te permitem mandar parte do processamento do JavaScript para uma thread separada. Você geralmente usaria uma worker para executar um processo pesado para que a UI não seja bloqueada.

- -
  Main thread: Tarefa A --> Tarefa C
-Worker thread: Tarefa pesada B
- -

Com isso em mente, dê uma olhada em simple-sync-worker.html (veja aqui), com o seu console JavaScript aberto. Isso é uma nova versão do nosso exemplo que calcula 10 milhões de datas em uma thread worker separada. Agora, quando você clica no botão, o navegador é capaz de mostrar o parágrafo antes que as datas sejam terminadas. A primeira operação não bloqueia a segunda.

- -

Código assíncrono

- -

Web workers podem ser bem úteis, mas elas tem as suas limitações. Uma delas é que elas não são capazes de acessar a {{Glossary("DOM")}} — você não pode fazer com que uma worker faça algo diretamente para atualizar a UI. Nós não poderíamos renderizar nossos 1 milhão de círculos azuis na nossa worker; basicamente ela pode apenas fazer cálculos de números.

- -

O segundo problema é que, mesmo que o código executado em uma worker não cause um bloqueio, ele ainda é um código síncrono. Isso se torna um problema quando uma função depende dos resultados de processos anteriores para funcionar. Considere os diagramas a seguir:

- -
Main thread: Tarefa A --> Tarefa B
- -

Nesse caso, digamos que a tarefa A está fazendo algo como pegar uma imagem do servidor e que a tarefa B faz algo com essa imagem, como colocar um filtro nela. Se você iniciar a tarefa A e depois tentar executar a tarefa B imediatamente, você obterá um erro, porque a imagem não estará disponível ainda.

- -
  Main thread: Tarefa A --> Tarefa B --> |Tarefa D|
-Worker thread: Tarefa C ---------------> |      |
- -

Neste caso, digamos que a tarefa D faz uso dos resultados das tarefas B e C. Se nós pudermos garantir que esses resultados estejam disponíveis ao mesmo tempo, então tudo talvez esteja bem, mas isso não é garantido. Se a tarefa D tentar ser executada quando um dos resultados não estiver disponível, ela retornará um erro.

- -

Para consertarmos tais problemas, os browsers nos permitem executar certas operações de modo assíncrono. Recursos como Promises te permitem executar uma operação e depois esperar pelo resultado antes de executar outra operação: 

- -
Main thread: Tarefa A                   Tarefa B
-    Promise:       |___operação async___|
- -

Já que a operação está acontecendo em outro lugar, a main thread não está bloqueada enquanto a operação assíncrona está sendo processada.

- -

Nós vamos começar a olhar em como podemos escrever código assíncrono no próximo artigo.

- -

Conclusão

- -

O design moderno de software gira em torno do uso de programação assíncrona, para permitir que os programas façam mais de uma coisa por vez. Ao usar APIs mais novas e mais poderosas, você encontrará mais casos em que a única maneira de fazer as coisas é assincronamente. Costumava ser difícil escrever código assíncrono. Ainda é preciso se acostumar, mas ficou muito mais fácil. No restante deste módulo, exploraremos ainda mais por que o código assíncrono é importante e como projetar o código que evita alguns dos problemas descritos acima.

- -

Nesse módulo

- - diff --git a/files/pt-br/learn/javascript/asynchronous/timeouts_and_intervals/index.html b/files/pt-br/learn/javascript/asynchronous/timeouts_and_intervals/index.html deleted file mode 100644 index f483c33937..0000000000 --- a/files/pt-br/learn/javascript/asynchronous/timeouts_and_intervals/index.html +++ /dev/null @@ -1,624 +0,0 @@ ---- -title: Timeouts e intervalos -slug: Learn/JavaScript/Asynchronous/Timeouts_and_intervals -translation_of: Learn/JavaScript/Asynchronous/Timeouts_and_intervals ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}
- -

Este tutorial é sobre os métodos tradicionais que o JavaScript tem disponíveis para executar códigos assíncronamente depois que um dado período de tempo tenha passado, ou em um intervalo (um número de segundos por segundo), discute suas utilidades e considera seus problemas.

- - - - - - - - - - - - -
Pré-requisitos:Entendimento básico sobre informáticas e fundamentos do JavaScript.
Objetivo:Entender loops e intervalos assíncronos e para o que eles servem.
- -

Introdução

- -

Por um longo tempo, a plataforma web tem oferecido à programadores JavaScript um número de funções que permitem que eles executem código assíncronamente depois de um determinado intervalo de tempo, e executar um bloco de código de modo assíncrono repetidamente até que você o mande parar.

- -

Essas funções são:

- -
-
setTimeout()
-
Executa um bloco específico uma vez depois de um determinado tempo
-
setInterval()
-
Executa um bloco específico repetidamente com um intervalo fixo entre cada chamada.
-
requestAnimationFrame()
-
Uma versão moderna de setInterval(). Ela executa um  bloc de código específico antes do navegador renderizar a tela novamento, permitindo que seja executada em uma taxa de quadros adequada, independentemente do ambiente em que está sendo executado.
-
- -

O código executado por estas funções é executado na main thread (depois do dado intervalo).

- -
-

É importante saber que você pode (e irá) executar outros códigos antes que uma chamada setTimeout() é executada, ou entre iterações de setInterval(). Dependendo de como essas operações são intensas, elas podem atrasar o seu código async ainda mais, já que o código async só é executado depois que a main thread terminar seu processamento (ou seja, quando a fila estiver vazia). Você aprenderá mais sobre isso enquanto fazemos nosso progresso neste artigo.

-
- -

De qualquer forma, essas funções são usadas para executar animações constantes e outros processamentos em um web site ou aplicação. Nas seções a seguir, nós vamos te mostrar como elas podem ser usadas.

- -

setTimeout()

- -

Como foi dito anteriormente, o setTimeout() executa um bloco de código particular depois que um determinado período de tempo passou. Ele toma os seguintes parâmetros:

- - - -
-

NOTA: O tempos especificafo não é o tempo garantido de execução, mas sim o tempo míniimo de execução. As callback que você passa para essas funções não podem ser executadas até que a main thread esteja vazia.

- -

Como consequência, códigos como setTimeout(fn, 0) serão executados assim que a fila estiver vazia, não imediatamente. Se você executar código como setTimeout(fn, 0) e depois imediatamente executar um loop que conta de 1 a 10 bilhões, sua callback será executada depois de alguns segundos.

-
- -

No exemplo a seguir, o navegador vai esperar dois segundos antes de executar a função anônima, e depois vai mostrar a mensagem de alerta (veja aqui, e veja o código):

- -
let myGreeting = setTimeout(function() {
-  alert('Hello, Mr. Universe!');
-}, 2000)
- -

As funções especificadas não tem que  ser anônimas. Você pode dar o nome da função, e até mesmo definir ela em outro lugar e passar uma referência para o timeout setTimeout(). As versões a seguir do código são equivalentes à primeira:

- -
// With a named function
-let myGreeting = setTimeout(function sayHi() {
-  alert('Hello, Mr. Universe!');
-}, 2000)
-
-// With a function defined separately
-function sayHi() {
-  alert('Hello Mr. Universe!');
-}
-
-let myGreeting = setTimeout(sayHi, 2000);
- -

Isso pode ser útil se você tem uma função que precisa ser chamada de um timeout e também em resposta à um evento, por exemplo. Mas também pode servir para manter seu código organizado, especialmente se a callback timetout é mais do que algumas linhas de código.

- -

setTimeout() retorna um valor identificador que pode ser usado para se referir ao timeout depois, como em quando você que pará-lo. Veja {{anch("Cancelando timetous")}} (abaixo) e aprenda como fazer isso.

- -

Passando parâmetros para uma função setTimeout()

- -

Quaisquer parâmetros que você quiser passar para a função sendo executada dentro do setTimeout() devem ser passados como parâmetros adicionais no final da lista.

- -

Por exemplo, você pode mudar a função anterior para que ela diga oi para qualquer nome que foi passada para ela:

- -
function sayHi(who) {
-  alert(`Hello ${who}!`);
-}
- -

Agora, você pode passar o nome da pessoa no setTimeout() como um terceiro parâmetro:

- -
let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');
- -

Cancelando timeouts

- -

Finalmente, se um timeout foi criado, você pode cancelá-lo antes que o tempo especificado tenha passado chamando clearTimeout(), passando para o identificador a chamada setTimeout() como um parâmetreo. então para cancelar o timeout acima, você fará isso:

- -
clearTimeout(myGreeting);
- -
-

Nota: Veja greeter-app.html para uma demonstração mais desenvolvida que te permite colocar o nome da pessoa a dizer oi em um formulário, e cancelar a saudação usando um botão separado (veja aqui o código fonte).

-
- -

setInterval()

- -

setTimeout() funciona perfeitamento quando você precisa executar algum código depois de um período de tempo. Mas o que acontece quando voc~e precisa executar o código de novo e de novo — por exemplo, no caso de uma animação?

- -

É aí que o setInterval() entra. Ele funciona de uma maneira muito similar à setTimeout(), exceto que a função que você passar como primeiro parâmetro é executada repetidamente em não menos que um número determinado de milissegundos dado no segundo parâmetro, ao invés de apenas uma vez. Você também pode passar qualquer parâmetro sendo executado como um parâmetro subsequente da chamada de setInterval().

- -

Vamos dar uma olhada em um exemplo. A função a seguir cria um novo objeto Date(), tira uma string de tempo usando toLocaleTimeString(), e depois a mostra naUI. Em seguida, ela executa a função uma vez por segundo usando setInterval(), criando o efeito de um relógio digital que é atualizado uma vez por segundo (veja aqui, e também veja o código):

- -
function displayTime() {
-   let date = new Date();
-   let time = date.toLocaleTimeString();
-   document.getElementById('demo').textContent = time;
-}
-
-const createClock = setInterval(displayTime, 1000);
- -

Assim como o setTimeout(), o setInterval() também retorna um valor identificador que você pode usar depois para cancelar o intervalo.

- -

Cancelando intervalos

- -

setInterval() continua sua execução para sempre, a menos que você faça algo sobre isso. Você provavelmente quer um jeito de parar tais tarefas, do contrário você pode acabar com error quando o navegador não puder completar outras versões futuras da tarefa, ou se a animação acabar. Você pode fazer isso do mesmo jeito que você para timeouts — passando o identificador retornado por setInterval() para a função clearInterval():

- -
const myInterval = setInterval(myFunction, 2000);
-
-clearInterval(myInterval);
- -

Aprendizado ativo: Criando seu próprio cronômetro!

- -

Com tudo isso dito, nós temos um desafio para você. Faça uma cópia do nosso exemplo setInterval-clock.html, e o modifique para criar seu próprio cronômetro.

- -

Você precisa mostrar um tempo na tela como antes, mas nesse exemplo você vai precisar de:

- - - -

Here's a few hints for you:

- - - -
-

Note: If you get stuck, you can find our version here (see the source code also).

-
- -

Coisas para se manter em mente sobre o setTimeout() e o setInterval()

- -

Existem algumas coisinhas que devemos sempre lembrar quando estamos trabalhando com setTimeout() esetInterval():

- -

Timeouts recursivos

- -

Há outra maneira de usar o  setTimeout(): você pode chamá-lo recusivamnete para executar o mesmo código repetidas vezes, ao invés de usar o setInterval().

- -

O exemplo abaixo usa um setTimeout() recursivo para executar a função passada a cada 100 millissegundos:

- -
let i = 1;
-
-setTimeout(function run() {
-  console.log(i);
-  i++;
-  setTimeout(run, 100);
-}, 100);
- -

Compare the above example to the following one — this uses setInterval() to accomplish the same effect:

- -
let i = 1;
-
-setInterval(function run() {
-  console.log(i);
-  i++
-}, 100);
- -

Qual a diferença entre o setTimeout() recursivo e o setInterval()?

- -

A diferença entre as duas versões é bem sútil.

- - - -

Quando seu código tem o potencial para levar mais tempo do que lhe foi atribuido, é melhor usar o setTimeout() recursivo — isso irá manter o intervalo de tempo constant entre execuções independente do quanto tempo o código levar para ser executado, e você não terá erros.

- -

Timeouts imediatos

- -

Usar zero como o valor para setTimeout() faz a execução da callback ser o mais rápido o possível, mas apenas depois que a main thread for terminada.

- -

Por exemplo, o código abaixo (veja funcionar aqui) mostra um alert que contém um "Hello", depois um  alert que contém "World" assim que você clicar em OK no primeiro alerta.

- -
setTimeout(function() {
-  alert('World');
-}, 0);
-
-alert('Hello');
- -

Isso pode ser útil em casos onde você quer fazer um bloco de código ser executado assim que a main thread acabar o seu processamento — colocar no loop de eventos async, assim ele vai ser executado logo depois.

- -

Cancelando com clearTimeout() ou clearInterval()

- -

clearTimeout() e clearInterval() usam a mesma lista de entradas para cancelamento. Isso significa que você pode usar os dois para cancelar um setTimeout() ou setInterval().

- -

Mas mesmo assim, você deve usar o clearTimeout() para entradas setTimeout() e clearInterval() para entradas setInterval(). Isso evita confusões.

- -

requestAnimationFrame()

- -

requestAnimationFrame() é uma função de loop especializada criada para executar animações com eficiência no navegador. Ela é basicamente a versão moderna de setInterval() — ela executa um bloco de código específico antes que o navegador renove o display, permitindo que uma animação seja executada em um framerate adequado independente do ambiente em que está sendo executada.

- -

Ela foi criada em resposta à problemas ocorridos com setInterval(), que por exemplo não roda em uma taxa de quadros otimizada para o dispositivo, e às vezes diminui os frames, continua a rodar mesmo se a guia não esiver ativa ou se a animação for rolada para fora da página, etc.

- -

(Leia mais sobre isso em CreativeJS.)

- -
-

Nota: Você pode encontrar exemplos do uso de requestAnimationFrame() em outros lugares do curso — por exemplo em Drawing graphics, e Object building practice.

-
- -

O método toma como argumentos uma callback a  ser invocada antes da renovação. Esse é o padrão geral que você verá usado em:

- -
function draw() {
-   // Drawing code goes here
-   requestAnimationFrame(draw);
-}
-
-draw();
- -

A ideia é definir uma função em que sua animação é atualizada (e.g. seus spritas se movem, a pontuação é atualizada, dados são recarregados, etc). Depois, você inicia o processo. No final do bloco da função você chama  requestAnimationFrame() com a referência da função passada como parâmetro, e isso instrui o navegador a chamar a função de novo na próxima renovação. Isso é executado continuamente, já que o código está chamando requestAnimationFrame() recursivamente.

- -
-

Nota: Se você quer realizar algum tipo de animação na DOM constantemente, Animações CSS são provavelemente mais rápidas. elas são calculadas diretamente pelo código interno do navegador, ao invés de JavaScript.

- -

Se, no entanto, você está fazendo algo mais complexo e envolvendo objetos que não são diretamente assessados da DOM (como 2D Canvas API ou objetos WebGL), requestAnimationFrame() é a melhor opção na maioria dos casos

-
- -

Qual a velocidade da sua animação?

- -

A suavidade da sua animação é diretamente dependente na frame rate da sua animação e é medida em frames per second (fps). The smoothness of your animation is directly dependent on your animation's frame rate and it is measured in frames per second (fps). Quanto maior esse número, mais suave será a sua animação, até certo ponto.

- -

Já que a maioria das tela tem uma taxa de atualização de 60Hz, a frame rate mais rápida que você pode ter é de 60fps quando trabalhando com web browsers. No entanto, mais frames significa mais processamento, o que pode ser causar uma queda de quadros e travamento.

- -

Se você tem um monitos com uma taxa de atualização de 60Hz e você quer atingir 60FPS você tem pelo menos 16.7 milissegundos (1000 / 60) para executar sua animação em cada frame. Isso é um lembrete de que você vai precisar estar atento à quantidade de código que você vai tentar executar em cada iteração do loop de animação.

- -

requestAnimationFrame() sempre tenta ficar o mais próximo possível de 60 FPS. Às vezes, isso não é possível — se você tem uma animação bem complexa e você está executando ela em um computador lento, sua frame rate será menor. Em todos os casos, o requestAnimationFrame() sempre vai fazer o melhor que pode com o que ele tem dísponivel.

- -

Como o requestAnimationFrame() se diferencia de setInterval() e setTimeout()?

- -

Vamos falar um pouco sobre como o método requestAnimationFrame() se diferencia dos outros métodos vistos anteriormente. Olhando com o código anterior:

- -
function draw() {
-   // Drawing code goes here
-   requestAnimationFrame(draw);
-}
-
-draw();
- -

Vamos ver isso usando o setInterval():

- -
function draw() {
-   // Drawing code goes here
-}
-
-setInterval(draw, 17);
- -

Como foi dito anteriormente, você não especifica um intervalo de tempo para requestAnimationFrame(). O método se executa o mais rápido e suave o possível nas condições atuais. O navegador também não perde tempo executando uma animação se ela está fora da tela por algum motivo, etc.

- -

setInterval(), por outro lado, exige que um  intervalo de tempo seja especificado. Nós chegamos ao valor final de 17 por meio da formula 1000 milliseconds / 60Hz, e depois arredondamos o resultado. Arredondar é uma boa ideia; se você tivesse arredondado para baixo, o navegador pode tentar executar a animação mais rápido do que 60  FPS, e não faria nenhuma diferênça na suavidade da animação de qualquer forma. Como foi dito antes, 60Hz é a taxa de atualização padrão.

- -

Incluindo um timestamp

- -

A callback passada para a função requestAnimationFrame() pode ser dada um parâmetro támbem: um valor timestamp, que representa o tempo desde que o  requestAnimationFrame() começou a rodar.

- -

Isso é útil, permite que você execute coisas em  um tempo específico e em passo constante, independente do quão rápido ou lento é o seu dispositivo. O padão geral que você usaria se parece um pouco com isso:

- -
let startTime = null;
-
-function draw(timestamp) {
-    if (!startTime) {
-      startTime = timestamp;
-    }
-
-   currentTime = timestamp - startTime;
-
-   // Do something based on current time
-
-   requestAnimationFrame(draw);
-}
-
-draw();
- -

Suporte do navegador

- -

requestAnimationFrame() é suportado em navegadores mais recentes do que setInterval()/setTimeout().  Curiosamente, está disponível no Internet Explorer 10 e além.

- -

Então, você não precisa dar suporte para versões mais velhas do IE, não há poruqe não usar o  requestAnimationFrame().

- -

Um exemplo simples

- -

Enough with the theory! Let's build your own personal requestAnimationFrame() example. You're going to create a simple "spinner animation"—the kind you might see displayed in an app when it is busy connecting to the server, etc.

- -
-

Note: In a real world example, you should probably use CSS animations to run this kind of simple animation. However, this kind of example is very useful to demonstrate requestAnimationFrame() usage, and you'd be more likely to use this kind of technique when doing something more complex such as updating the display of a game on each frame.

-
- -
    -
  1. -

    Grab a basic HTML template (such as this one).

    -
  2. -
  3. -

    Put an empty {{htmlelement("div")}} element inside the {{htmlelement("body")}}, then add a ↻ character inside it. This is circular arrow character will act as our spinner for this example.

    -
  4. -
  5. -

    Apply the following CSS to the HTML template (in whatever way you prefer). This sets a red background on the page, sets the <body> height to 100% of the {{htmlelement("html")}} height, and centers the <div> inside the <body>, horizontally and vertically.

    - -
    html {
    -  background-color: white;
    -  height: 100%;
    -}
    -
    -body {
    -  height: inherit;
    -  background-color: red;
    -  margin: 0;
    -  display: flex;
    -  justify-content: center;
    -  align-items: center;
    -}
    -
    -div {
    -  display: inline-block;
    -  font-size: 10rem;
    -}
    -
  6. -
  7. -

    Insert a {{htmlelement("script")}} element just above the </body> tag.

    -
  8. -
  9. -

    Insert the following JavaScript inside your <script> element. Here, you're storing a reference to the <div> inside a constant, setting a rotateCount variable to 0, setting an uninitialized variable that will later be used to contain a reference to the requestAnimationFrame() call, and setting a startTime variable to null, which will later be used to store the start time of the requestAnimationFrame().

    - -
    const spinner = document.querySelector('div');
    -let rotateCount = 0;
    -let startTime = null;
    -let rAF;
    -
    -
  10. -
  11. -

    Below the previous code, insert a draw() function that will be used to contain our animation code, which includes the timestamp parameter:

    - -
    function draw(timestamp) {
    -
    -}
    -
  12. -
  13. -

    Inside draw(), add the following lines. They will define the start time if it is not defined already (this will only happen on the first loop iteration), and set the rotateCount to a value to rotate the spinner by (the current timestamp, take the starting timestamp, divided by three so it doesn't go too fast):

    - -
      if (!startTime) {
    -   startTime = timestamp;
    -  }
    -
    -  rotateCount = (timestamp - startTime) / 3;
    -
    -
  14. -
  15. -

    Below the previous line inside draw(), add the following block — this checks to see if the value of rotateCount is above 359 (e.g. 360, a full circle). If so, it sets the value to its modulo of 360 (i.e. the remainder left over when the value is divided by 360) so the circle animation can continue uninterrupted, at a sensible, low value. Note that this isn't strictly necessary, but it is easier to work with values of 0–359 degrees than values like "128000 degrees".

    - -
    if (rotateCount > 359) {
    -  rotateCount %= 360;
    -}
    -
  16. -
  17. Next, below the previous block add the following line to actually rotate the spinner: -
    spinner.style.transform = `rotate(${rotateCount}deg)`;
    -
  18. -
  19. -

    At the very bottom inside the draw() function, insert the following line. This is the key to the whole operation — you are setting the variable defined earlier to an active requestAnimation() call, which takes the draw() function as its parameter. This starts the animation off, constantly running the draw() function at a rate as near 60 FPS as possible.

    - -
    rAF = requestAnimationFrame(draw);
    -
  20. -
- -
-

Note: You can find this example live on GitHub. (You can see the source code, also.)

-
- -

Clearing a requestAnimationFrame() call

- -

Clearing a requestAnimationFrame() call can be done by calling the corresponding cancelAnimationFrame() method. (Note that the function name starts with "cancel", not "clear" as with the "set..." methods.) 

- -

Just pass it the value returned by the requestAnimationFrame() call to cancel, which you stored in the variable rAF:

- -
cancelAnimationFrame(rAF);
- -

Active learning: Starting and stopping our spinner

- -

In this exercise, we'd like you to test out the cancelAnimationFrame() method by taking our previous example and updating it, adding an event listener to start and stop the spinner when the mouse is clicked anywhere on the page.

- -

Some hints:

- - - -
-

Note: Try this yourself first; if you get really stuck, check out of our live example and source code.

-
- -

Throttling a requestAnimationFrame() animation

- -

One limitation of requestAnimationFrame() is that you can't choose your frame rate. This isn't a problem most of the time, as generally you want your animation to run as smoothly as possible. But what about when you want to create an old school, 8-bit-style animation?

- -

This was a problem, for example, in the Monkey Island-inspired walking animation from our Drawing Graphics article:

- -

{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation.html", '100%', 260)}}

- -

In this example, you have to animate both the position of the character on the screen, and the sprite being shown. There are only 6 frames in the sprite's animation. If you showed a different sprite frame for every frame displayed on the screen by requestAnimationFrame(), Guybrush would move his limbs too fast and the animation would look ridiculous. This example therefore throttles the rate at which the sprite cycles its frames using the following code:

- -
if (posX % 13 === 0) {
-  if (sprite === 5) {
-    sprite = 0;
-  } else {
-    sprite++;
-  }
-}
- -

So the code only cycles the sprite once every 13 animation frames.

- -

...Actually, it's about every 6.5 frames, as we update posX (character's position on the screen) by two each frame:

- -
if (posX > width/2) {
-  newStartPos = -( (width/2) + 102 );
-  posX = Math.ceil(newStartPos / 13) * 13;
-  console.log(posX);
-} else {
-  posX += 2;
-}
- -

This is the code that calculates how to update the position in each animation frame.

- -

The method you use to throttle your animation will depend on your particular code. For instance, in the earlier spinner example, you could make it appear to move slower by only increasing rotateCount by one on each frame, instead of two.

- -

Active learning: a reaction game

- -

For the final section of this article, you'll create a 2-player reaction game. The game will have two players, one of whom controls the game using the A key, and the other with the L key.

- -

When the Start button is pressed, a spinner like the one we saw earlier is displayed for a random amount of time between 5 and 10 seconds. After that time, a message will appear saying "PLAYERS GO!!" — once this happens, the first player to press their control button will win the game.

- -

{{EmbedGHLiveSample("learning-area/javascript/asynchronous/loops-and-intervals/reaction-game.html", '100%', 500)}}

- -

Let's work through this:

- -
    -
  1. -

    First of all, download the starter file for the app. This contains the finished HTML structure and CSS styling, giving us a game board that shows the two players' information (as seen above), but with the spinner and results paragraph displayed on top of one another. You just have to write the JavaScript code.

    -
  2. -
  3. -

    Inside the empty {{htmlelement("script")}} element on your page, start by adding the following lines of code that define some constants and variables you'll need in the rest of the code:

    - -
    const spinner = document.querySelector('.spinner p');
    -const spinnerContainer = document.querySelector('.spinner');
    -let rotateCount = 0;
    -let startTime = null;
    -let rAF;
    -const btn = document.querySelector('button');
    -const result = document.querySelector('.result');
    - -

    In order, these are:

    - -
      -
    1. A reference to the spinner, so you can animate it.
    2. -
    3. A reference to the {{htmlelement("div")}} element that contains the spinner, used for showing and hiding it.
    4. -
    5. A rotate count. This determines how much you want to show the spinner rotated on each frame of the animation.
    6. -
    7. A null start time. This will be populated with a start time when the spinner starts spinning.
    8. -
    9. An uninitialized variable to later store the {{domxref("Window.requestAnimationFrame", "requestAnimationFrame()")}} call that animates the spinner.
    10. -
    11. A reference to the Start button.
    12. -
    13. A reference to the results paragraph.
    14. -
    -
  4. -
  5. -

    Next, below the previous lines of code, add the following function. It simply takes two numbers and returns a random number between the two. You'll need this to generate a random timeout interval later on.

    - -
    function random(min,max) {
    -  var num = Math.floor(Math.random()*(max-min)) + min;
    -  return num;
    -}
    -
  6. -
  7. -

    Next add  the draw() function, which animates the spinner. This is very similar to the version from the simple spinner example, earlier:

    - -
    function draw(timestamp) {
    -  if(!startTime) {
    -   startTime = timestamp;
    -  }
    -
    -  rotateCount = (timestamp - startTime) / 3;
    -
    -  if(rotateCount > 359) {
    -    rotateCount %= 360;
    -  }
    -
    -  spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
    -  rAF = requestAnimationFrame(draw);
    -}
    -
  8. -
  9. -

    Now it is time to set up the initial state of the app when the page first loads. Add the following two lines, which simply hide the results paragraph and spinner container using display: none;.

    - -
    result.style.display = 'none';
    -spinnerContainer.style.display = 'none';
    -
  10. -
  11. -

    Next, define a reset() function, which sets the app back to the original state required to start the game again after it has been played. Add the following at the bottom of your code:

    - -
    function reset() {
    -  btn.style.display = 'block';
    -  result.textContent = '';
    -  result.style.display = 'none';
    -}
    -
  12. -
  13. -

    Okay, enough preparation!  It's time to make the game playable! Add the following block to your code. The start() function calls draw() to start the spinner spinning and display it in the UI, hides the Start button so you can't mess up the game by starting it multiple times concurrently, and runs a setTimeout() call that runs a setEndgame() function after a random interval between 5 and 10 seconds has passed. The following block also adds an event listener to your button to run the start() function when it is clicked.

    - -
    btn.addEventListener('click', start);
    -
    -function start() {
    -  draw();
    -  spinnerContainer.style.display = 'block';
    -  btn.style.display = 'none';
    -  setTimeout(setEndgame, random(5000,10000));
    -}
    - -
    -

    Note: You'll see this example is calling setTimeout() without storing the return value. (So, not let myTimeout = setTimeout(functionName, interval).) 

    - -

    This works just fine, as long as you don't need to clear your interval/timeout at any point. If you do, you'll need to save the returned identifier!

    -
    - -

    The net result of the previous code is that when the Start button is pressed, the spinner is shown and the players are made to wait a random amount of time before they are asked to press their button. This last part is handled by the setEndgame() function, which you'll define next.

    -
  14. -
  15. -

    Add the following function to your code next:

    - -
    function setEndgame() {
    -  cancelAnimationFrame(rAF);
    -  spinnerContainer.style.display = 'none';
    -  result.style.display = 'block';
    -  result.textContent = 'PLAYERS GO!!';
    -
    -  document.addEventListener('keydown', keyHandler);
    -
    -  function keyHandler(e) {
    -    console.log(e.key);
    -    if(e.key === 'a') {
    -      result.textContent = 'Player 1 won!!';
    -    } else if(e.key === 'l') {
    -      result.textContent = 'Player 2 won!!';
    -    }
    -
    -    document.removeEventListener('keydown', keyHandler);
    -    setTimeout(reset, 5000);
    -  };
    -}
    - -

    Stepping through this:

    - -
      -
    1. First, cancel the spinner animation with {{domxref("window.cancelAnimationFrame", "cancelAnimationFrame()")}} (it is always good to clean up unneeded processes), and hide the spinner container.
    2. -
    3. Next, display the results paragraph and set its text content to "PLAYERS GO!!" to signal to the players that they can now press their button to win.
    4. -
    5. Attach a keydown event listener to the document. When any button is pressed down, the keyHandler() function is run.
    6. -
    7. Inside keyHandler(), the code includes the event object as a parameter (represented by e) — its {{domxref("KeyboardEvent.key", "key")}} property contains the key that was just pressed, and you can use this to respond to specific key presses with specific actions.
    8. -
    9. Log e.key to the console, which is a useful way of finding out the key value of different keys you are pressing.
    10. -
    11. When e.key is "a", display a message to say that Player 1 won, and when e.key is "l", display a message to say Player 2 won. (Note: This will only work with lowercase a and l — if an uppercase A or L is submitted (the key plus Shift), it is counted as a different key!)
    12. -
    13. Regardless of which one of the player control keys was pressed,  remove the keydown event listener using {{domxref("EventTarget.removeEventListener", "removeEventListener()")}} so that once the winning press has happened, no more keyboard input is possible to mess up the final game result. You also use setTimeout() to call reset() after 5 seconds — as explained earlier, this function resets the game back to its original state so that a new game can be started.
    14. -
    -
  16. -
- -

That's it—you're all done!

- -
-

Note: If you get stuck, check out our version of the reaction game (see the source code also).

-
- -

Conclusion

- -

So that's it — all the essentials of async loops and intervals covered in one article. You'll find these methods useful in a lot of situations, but take care not to overuse them! Because they still run on the main thread, heavy and intensive callbacks (especially those that manipulate the DOM) can really slow down a page if you're not careful.

- -

{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}

- -

In this module

- - -- cgit v1.2.3-54-g00ecf