aboutsummaryrefslogtreecommitdiff
path: root/files/zh-tw/learn
diff options
context:
space:
mode:
Diffstat (limited to 'files/zh-tw/learn')
-rw-r--r--files/zh-tw/learn/javascript/asynchronous/async_await/index.html435
-rw-r--r--files/zh-tw/learn/javascript/asynchronous/choosing_the_right_approach/index.html536
-rw-r--r--files/zh-tw/learn/javascript/asynchronous/concepts/index.html169
-rw-r--r--files/zh-tw/learn/javascript/asynchronous/timeouts_and_intervals/index.html646
4 files changed, 0 insertions, 1786 deletions
diff --git a/files/zh-tw/learn/javascript/asynchronous/async_await/index.html b/files/zh-tw/learn/javascript/asynchronous/async_await/index.html
deleted file mode 100644
index 3c594daf2a..0000000000
--- a/files/zh-tw/learn/javascript/asynchronous/async_await/index.html
+++ /dev/null
@@ -1,435 +0,0 @@
----
-title: 利用 async 及 await 讓非同步程式設計變得更容易
-slug: Learn/JavaScript/Asynchronous/Async_await
-tags:
- - Beginner
- - CodingScripting
- - Guide
- - JavaScript
- - Learn
- - Promises
- - async
- - asynchronous
- - await
----
-<div>{{LearnSidebar}}</div>
-
-<div>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous/Choosing_the_right_approach", "Learn/JavaScript/Asynchronous")}}</div>
-
-<p class="summary">More recent additions to the JavaScript language are <a href="/en-US/docs/Web/JavaScript/Reference/Statements/async_function">async functions</a> and the <code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/await">await</a></code> keyword, part of the so-called ECMAScript 2017 JavaScript edition (see <a href="/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_Next_support_in_Mozilla">ECMAScript Next support in Mozilla</a>). These features basically act as syntactic sugar on top of promises, making asynchronous code easier to write and to read afterwards. They make async code look more like old-school synchronous code, so they're well worth learning. This article gives you what you need to know.</p>
-
-<table class="learn-box standard-table">
- <tbody>
- <tr>
- <th scope="row">Prerequisites:</th>
- <td>Basic computer literacy, a reasonable understanding of JavaScript fundamentals, an understanding of async code in general and promises.</td>
- </tr>
- <tr>
- <th scope="row">Objective:</th>
- <td>To understand promises and how to use them.</td>
- </tr>
- </tbody>
-</table>
-
-<h2 id="The_basics_of_asyncawait">The basics of async/await</h2>
-
-<p>There are two parts to using async/await in your code.</p>
-
-<h3 id="The_async_keyword">The async keyword</h3>
-
-<p>First of all we have the <code>async</code> keyword, which you put in front of a function declaration to turn it into an <a href="/en-US/docs/Web/JavaScript/Reference/Statements/async_function">async function</a>. An async function is a function that knows how to expect the possibility of the <code>await</code> keyword being used to invoke asynchronous code.</p>
-
-<p>Try typing the following lines into your browser's JS console:</p>
-
-<pre class="brush: js">function hello() { return "Hello" };
-hello();</pre>
-
-<p>The function returns "Hello" — nothing special, right?</p>
-
-<p>But what if we turn this into an async function? Try the following:</p>
-
-<pre class="brush: js">async function hello() { return "Hello" };
-hello();</pre>
-
-<p>Ah. Invoking the function now returns a promise. This is one of the traits of async functions — their return values are guaranteed to be converted to promises.</p>
-
-<p>You can also create an <a href="/en-US/docs/Web/JavaScript/Reference/Operators/async_function">async function expression</a>, like so:</p>
-
-<pre class="brush: js">let hello = async function() { return "Hello" };
-hello();</pre>
-
-<p>And you can use arrow functions:</p>
-
-<pre class="brush: js">let hello = async () =&gt; { return "Hello" };</pre>
-
-<p>These all do basically the same thing.</p>
-
-<p>To actually consume the value returned when the promise fulfills, since it is returning a promise, we could use a <code>.then()</code> block:</p>
-
-<pre class="brush: js">hello().then((value) =&gt; console.log(value))</pre>
-
-<p>or even just shorthand such as</p>
-
-<pre class="brush: js">hello().then(console.log)</pre>
-
-<p>Like we saw in the last article.</p>
-
-<p>So the <code>async</code> keyword is added to functions to tell them to return a promise rather than directly returning the value.</p>
-
-<h3 id="The_await_keyword">The await keyword</h3>
-
-<p>The advantage of an async function only becomes apparent when you combine it with the <a href="/en-US/docs/Web/JavaScript/Reference/Operators/await">await</a> keyword. <code>await</code> only works inside async functions within regular JavaScript code, however it can be used on its own with <a href="/en-US/docs/Web/JavaScript/Guide/Modules">JavaScript modules.</a></p>
-
-<p><code>await</code> can be put in front of any async promise-based function to pause your code on that line until the promise fulfills, then return the resulting value.</p>
-
-<p>You can use <code>await</code> when calling any function that returns a Promise, including web API functions.</p>
-
-<p>Here is a trivial example:</p>
-
-<pre class="brush: js">async function hello() {
- return greeting = await Promise.resolve("Hello");
-};
-
-hello().then(alert);</pre>
-
-<p>Of course, the above example is not very useful, although it does serve to illustrate the syntax. Let's move on and look at a real example.</p>
-
-<h2 id="Rewriting_promise_code_with_asyncawait">Rewriting promise code with async/await</h2>
-
-<p>Let's look back at a simple fetch example that we saw in the previous article:</p>
-
-<pre class="brush: js">fetch('coffee.jpg')
-.then(response =&gt; {
-  if (!response.ok) {
-    throw new Error(`HTTP error! status: ${response.status}`);
-  }
-  return response.blob();
-})
-.then(myBlob =&gt; {
- let objectURL = URL.createObjectURL(myBlob);
- let image = document.createElement('img');
- image.src = objectURL;
- document.body.appendChild(image);
-})
-.catch(e =&gt; {
- console.log('There has been a problem with your fetch operation: ' + e.message);
-});</pre>
-
-<p>By now, you should have a reasonable understanding of promises and how they work, but let's convert this to use async/await to see how much simpler it makes things:</p>
-
-<pre class="brush: js">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 =&gt; {
- console.log('There has been a problem with your fetch operation: ' + e.message);
-});</pre>
-
-<p>It makes code much simpler and easier to understand — no more <code>.then()</code> blocks everywhere!</p>
-
-<p>Since an <code>async</code> keyword turns a function into a promise, you could refactor your code to use a hybrid approach of promises and await, bringing the second half of the function out into a new block to make it more flexible:</p>
-
-<pre class="brush: js">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) =&gt; {
- let objectURL = URL.createObjectURL(blob);
- let image = document.createElement('img');
- image.src = objectURL;
- document.body.appendChild(image);
-}).catch(e =&gt; console.log(e));</pre>
-
-<p>You can try typing in the example yourself, or running our <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/simple-fetch-async-await.html">live example</a> (see also the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/simple-fetch-async-await.html">source code</a>).</p>
-
-<h3 id="But_how_does_it_work">But how does it work?</h3>
-
-<p>You'll note that we've wrapped the code inside a function, and we've included the <code>async</code> keyword before the <code>function</code> keyword. This is necessary — you have to create an async function to define a block of code in which you'll run your async code; as we said earlier, <code>await</code> only works inside of async functions.</p>
-
-<p>Inside the <code>myFetch()</code> function definition you can see that the code closely resembles the previous promise version, but there are some differences. Instead of needing to chain a <code>.then()</code> block on to the end of each promise-based method, you just need to add an <code>await</code> keyword before the method call, and then assign the result to a variable. The <code>await</code> keyword causes the JavaScript runtime to pause your code on this line, not allowing further code to execute in the meantime until the async function call has returned its result — very useful if subsequent code relies on that result!</p>
-
-<p>Once that's complete, your code continues to execute starting on the next line. For example:</p>
-
-<pre class="brush: js">let response = await fetch('coffee.jpg');</pre>
-
-<p>The response returned by the fulfilled <code>fetch()</code> promise is assigned to the <code>response</code> variable when that response becomes available, and the parser pauses on this line until that occurs. Once the response is available, the parser moves to the next line, which creates a <code><a href="/en-US/docs/Web/API/Blob">Blob</a></code> out of it. This line also invokes an async promise-based method, so we use <code>await</code> here as well. When the result of operation returns, we return it out of the <code>myFetch()</code> function.</p>
-
-<p>This means that when we call the <code>myFetch()</code> function, it returns a promise, so we can chain a <code>.then()</code> onto the end of it inside which we handle displaying the blob onscreen.</p>
-
-<p>You are probably already thinking "this is really cool!", and you are right — fewer <code>.then()</code> blocks to wrap around code, and it mostly just looks like synchronous code, so it is really intuitive.</p>
-
-<h3 id="Adding_error_handling">Adding error handling</h3>
-
-<p>And if you want to add error handling, you've got a couple of options.</p>
-
-<p>You can use a synchronous <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/try...catch">try...catch</a></code> structure with <code>async</code>/<code>await</code>. This example expands on the first version of the code we showed above:</p>
-
-<pre class="brush: js">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();</pre>
-
-<p>The <code>catch() {}</code> block is passed an error object, which we've called <code>e</code>; we can now log that to the console, and it will give us a detailed error message showing where in the code the error was thrown.</p>
-
-<p>If you wanted to use the second (refactored) version of the code that we showed above, you would be better off just continuing the hybrid approach and chaining a <code>.catch()</code> block onto the end of the <code>.then()</code> call, like this:</p>
-
-<pre class="brush: js">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) =&gt; {
- let objectURL = URL.createObjectURL(blob);
- let image = document.createElement('img');
- image.src = objectURL;
- document.body.appendChild(image);
-})
-.catch((e) =&gt;
- console.log(e)
-);</pre>
-
-<p>This is because the <code>.catch()</code> block will catch errors occurring in both the async function call and the promise chain. If you used the <code>try</code>/<code>catch</code> block here, you might still get unhandled errors in the <code>myFetch()</code> function when it's called.</p>
-
-<p>You can find both of these examples on GitHub:</p>
-
-<ul>
- <li><a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/simple-fetch-async-await-try-catch.html">simple-fetch-async-await-try-catch.html</a> (see <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/simple-fetch-async-await-try-catch.html">source code</a>)</li>
- <li><a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/simple-fetch-async-await-promise-catch.html">simple-fetch-async-await-promise-catch.html</a> (see <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/simple-fetch-async-await-promise-catch.html">source code</a>)</li>
-</ul>
-
-<h2 id="Awaiting_a_Promise.all">Awaiting a Promise.all()</h2>
-
-<p>async/await is built on top of <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">promises</a>, so it's compatible with all the features offered by promises. This includes <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all">Promise.all()</a></code> — you can quite happily await a <code>Promise.all()</code> call to get all the results returned into a variable in a way that looks like simple synchronous code. Again, let's return to <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/promise-all.html">an example we saw in our previous article</a>. Keep it open in a separate tab so you can compare and contrast with the new version shown below.</p>
-
-<p>Converting this to async/await (see <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/promise-all-async-await.html">live demo</a> and <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/promise-all-async-await.html">source code</a>), this now looks like so:</p>
-
-<pre class="brush: js">async function fetchAndDecode(url, type) {
- let response = await fetch(url);
-
- let content;
-
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- 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) =&gt;
- console.log(e)
-);</pre>
-
-<p>You'll see that the <code>fetchAndDecode()</code> function has been converted easily into an async function with just a few changes. See the <code>Promise.all()</code> line:</p>
-
-<pre class="brush: js">let values = await Promise.all([coffee, tea, description]);</pre>
-
-<p>By using <code>await</code> here we are able to get all the results of the three promises returned into the <code>values</code> array, when they are all available, in a way that looks very much like sync code. We've had to wrap all the code in a new async function, <code>displayContent()</code>, and we've not reduced the code by a lot of lines, but being able to move the bulk of the code out of the <code>.then()</code> block provides a nice, useful simplification, leaving us with a much more readable program.</p>
-
-<p>For error handling, we've included a <code>.catch()</code> block on our <code>displayContent()</code> call; this will handle errors occurring in both functions.</p>
-
-<div class="notecard note">
-<p><strong>Note</strong>: It is also possible to use a sync <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/try...catch#the_finally_clause">finally</a></code> block within an async function, in place of a <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally">.finally()</a></code> async block, to show a final report on how the operation went — you can see this in action in our <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/promise-finally-async-await.html">live example</a> (see also the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/promise-finally-async-await.html">source code</a>).</p>
-</div>
-
-<h2 id="The_downsides_of_asyncawait">The downsides of async/await</h2>
-
-<p>Async/await is really useful to know about, but there are a couple of downsides to consider.</p>
-
-<p>Async/await makes your code look synchronous, and in a way it makes it behave more synchronously. The <code>await</code> keyword blocks execution of all the code that follows it until the promise fulfills, exactly as it would with a synchronous operation. It does allow other tasks to continue to run in the meantime, but the awaited code is blocked.</p>
-
-<pre class="brush: js">async function makeResult(items) {
- let newArr = [];
- for(let i=0; i &lt; 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
-}
-
-</pre>
-
-<p>This means that your code could be slowed down by a significant number of awaited promises happening straight after one another. Each <code>await</code> will wait for the previous one to finish, whereas actually what you want is for the promises to begin processing simultaneously, like they would do if we weren't using async/await.</p>
-
-<p>There is a pattern that can mitigate this problem — setting off all the promise processes by storing the <code>Promise</code> objects in variables, and then awaiting them all afterwards. Let's have a look at some examples that prove the concept.</p>
-
-<p>We've got two examples available — <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/slow-async-await.html">slow-async-await.html</a> (see <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/slow-async-await.html">source code</a>) and <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/fast-async-await.html">fast-async-await.html</a> (see <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/fast-async-await.html">source code</a>). Both of them start off with a custom promise function that fakes an async process with a <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout()</a></code> call:</p>
-
-<pre class="brush: js">function timeoutPromise(interval) {
- return new Promise((resolve, reject) =&gt; {
- setTimeout(function(){
- resolve("done");
- }, interval);
- });
-};</pre>
-
-<p>Then each one includes a <code>timeTest()</code> async function that awaits three <code>timeoutPromise()</code> calls:</p>
-
-<pre class="brush: js">async function timeTest() {
- ...
-}</pre>
-
-<p>Each one ends by recording a start time, seeing how long the <code>timeTest()</code> promise takes to fulfill, then recording an end time and reporting how long the operation took in total:</p>
-
-<pre class="brush: js">let startTime = Date.now();
-timeTest().then(() =&gt; {
- let finishTime = Date.now();
- let timeTaken = finishTime - startTime;
- alert("Time taken in milliseconds: " + timeTaken);
-})</pre>
-
-<p>It is the <code>timeTest()</code> function that differs in each case.</p>
-
-<p>In the <code>slow-async-await.html</code> example, <code>timeTest()</code> looks like this:</p>
-
-<pre class="brush: js">async function timeTest() {
- await timeoutPromise(3000);
- await timeoutPromise(3000);
- await timeoutPromise(3000);
-}</pre>
-
-<p>Here we await all three <code>timeoutPromise()</code> calls directly, making each one alert for 3 seconds. Each subsequent one is forced to wait until the last one finished — if you run the first example, you'll see the alert box reporting a total run time of around 9 seconds.</p>
-
-<p>In the <code>fast-async-await.html</code> example, <code>timeTest()</code> looks like this:</p>
-
-<pre class="brush: js">async function timeTest() {
- const timeoutPromise1 = timeoutPromise(3000);
- const timeoutPromise2 = timeoutPromise(3000);
- const timeoutPromise3 = timeoutPromise(3000);
-
- await timeoutPromise1;
- await timeoutPromise2;
- await timeoutPromise3;
-}</pre>
-
-<p>Here we store the three <code>Promise</code> objects in variables, which has the effect of setting off their associated processes all running simultaneously.</p>
-
-<p>Next, we await their results — because the promises all started processing at essentially the same time, the promises will all fulfill at the same time; when you run the second example, you'll see the alert box reporting a total run time of just over 3 seconds!</p>
-
-<p>You'll have to test your code carefully, and bear this in mind if performance starts to suffer.</p>
-
-<p>Another minor inconvenience is that you have to wrap your awaited promises inside an async function.</p>
-
-<h2 id="Asyncawait_class_methods">Async/await class methods</h2>
-
-<p>As a final note before we move on, you can even add <code>async</code> in front of class/object methods to make them return promises, and <code>await</code> promises inside them. Take a look at the <a href="/en-US/docs/Learn/JavaScript/Objects/Inheritance#ecmascript_2015_classes">ES class code we saw in our object-oriented JavaScript article</a>, and then look at our modified version with an <code>async</code> method:</p>
-
-<pre class="brush: js">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']);</pre>
-
-<p>The first class method could now be used something like this:</p>
-
-<pre class="brush: js">han.greeting().then(console.log);</pre>
-
-<h2 id="Browser_support">Browser support</h2>
-
-<p>One consideration when deciding whether to use async/await is support for older browsers. They are available in modern versions of most browsers, the same as promises; the main support problems come with Internet Explorer and Opera Mini.</p>
-
-<p>If you want to use async/await but are concerned about older browser support, you could consider using the <a href="https://babeljs.io/">BabelJS</a> library — this allows you to write your applications using the latest JavaScript and let Babel figure out what changes if any are needed for your user’s browsers. On encountering a browser that does not support async/await, Babel's polyfill can automatically provide fallbacks that work in older browsers.</p>
-
-<h2 id="Conclusion">Conclusion</h2>
-
-<p>And there you have it — async/await provide a nice, simplified way to write async code that is simpler to read and maintain. Even with browser support being more limited than other async code mechanisms at the time of writing, it is well worth learning and considering for use, both for now and in the future.</p>
-
-<p>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous/Choosing_the_right_approach", "Learn/JavaScript/Asynchronous")}}</p>
-
-<h2 id="In_this_module">In this module</h2>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">General asynchronous programming concepts</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">Introducing asynchronous JavaScript</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">Graceful asynchronous programming with Promises</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">Making asynchronous programming easier with async and await</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">Choosing the right approach</a></li>
-</ul>
diff --git a/files/zh-tw/learn/javascript/asynchronous/choosing_the_right_approach/index.html b/files/zh-tw/learn/javascript/asynchronous/choosing_the_right_approach/index.html
deleted file mode 100644
index 6099d470fa..0000000000
--- a/files/zh-tw/learn/javascript/asynchronous/choosing_the_right_approach/index.html
+++ /dev/null
@@ -1,536 +0,0 @@
----
-title: 選擇正確的方法
-slug: Learn/JavaScript/Asynchronous/Choosing_the_right_approach
-tags:
- - Beginner
- - Intervals
- - JavaScript
- - Learn
- - Optimize
- - Promises
- - async
- - asynchronous
- - await
- - requestAnimationFrame
- - setInterval
- - setTimeout
- - timeouts
----
-<div>{{LearnSidebar}}</div>
-
-<div>{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}</div>
-
-<p>在結束本單元前,我們將會從先前所討論過的不同程式技巧和功能,幫助您考量在甚麼情況下應該使用哪一個,並適當的提供一些有關常見的陷阱的建議及提醒。</p>
-
-<table class="learn-box standard-table">
- <tbody>
- <tr>
- <th scope="row">Prerequisites:</th>
- <td>Basic computer literacy, a reasonable understanding of JavaScript fundamentals.</td>
- </tr>
- <tr>
- <th scope="row">Objective:</th>
- <td>To be able to make a sound choice of when to use different asynchronous programming techniques.</td>
- </tr>
- </tbody>
-</table>
-
-<h2 id="Asynchronous_callbacks">Asynchronous callbacks</h2>
-
-<p>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.</p>
-
-<table class="standard-table">
- <caption>Useful for...</caption>
- <thead>
- <tr>
- <th scope="col">Single delayed operation</th>
- <th scope="col">Repeating operation</th>
- <th scope="col">Multiple sequential operations</th>
- <th scope="col">Multiple simultaneous operations</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>No</td>
- <td>Yes (recursive callbacks)</td>
- <td>Yes (nested callbacks)</td>
- <td>No</td>
- </tr>
- </tbody>
-</table>
-
-<h3 id="Code_example">Code example</h3>
-
-<p>An example that loads a resource via the <a href="/en-US/docs/Web/API/XMLHttpRequest"><code>XMLHttpRequest</code> API</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/xhr-async-callback.html">run it live</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/xhr-async-callback.html">see the source</a>):</p>
-
-<pre class="brush: js">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);</pre>
-
-<h3 id="Pitfalls">Pitfalls</h3>
-
-<ul>
- <li>Nested callbacks can be cumbersome and hard to read (i.e. "callback hell").</li>
- <li>Failure callbacks need to be called once for each level of nesting, whereas with promises you can just use a single <code>.catch()</code> block to handle the errors for the entire chain.</li>
- <li>Async callbacks just aren't very graceful.</li>
- <li>Promise callbacks are always called in the strict order they are placed in the event queue; async callbacks aren't.</li>
- <li>Async callbacks lose full control of how the function will be executed when passed to a third-party library.</li>
-</ul>
-
-<h3 id="Browser_compatibility">Browser compatibility</h3>
-
-<p>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.</p>
-
-<h3 id="Further_information">Further information</h3>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">Introducing asynchronous JavaScript</a>, in particular <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing#async_callbacks">Async callbacks</a></li>
-</ul>
-
-<h2 id="setTimeout">setTimeout()</h2>
-
-<p><code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout()</a></code> is a method that allows you to run a function after an arbitrary amount of time has passed.</p>
-
-<table class="standard-table">
- <caption>Useful for...</caption>
- <thead>
- <tr>
- <th scope="col">Single delayed operation</th>
- <th scope="col">Repeating operation</th>
- <th scope="col">Multiple sequential operations</th>
- <th scope="col">Multiple simultaneous operations</th>
- </tr>
- <tr>
- <td>Yes</td>
- <td>Yes (recursive timeouts)</td>
- <td>Yes (nested timeouts)</td>
- <td>No</td>
- </tr>
- </thead>
-</table>
-
-<h3 id="Code_example_2">Code example</h3>
-
-<p>Here the browser will wait two seconds before executing the anonymous function, then will display the alert message (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/simple-settimeout.html">see it running live</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/simple-settimeout.html">see the source code</a>):</p>
-
-<pre class="brush: js">let myGreeting = setTimeout(function() {
- alert('Hello, Mr. Universe!');
-}, 2000)</pre>
-
-<h3 id="Pitfalls_2">Pitfalls</h3>
-
-<p>You can use recursive <code>setTimeout()</code> calls to run a function repeatedly in a similar fashion to <code>setInterval()</code>, using code like this:</p>
-
-<pre class="brush: js">let i = 1;
-setTimeout(function run() {
- console.log(i);
- i++;
-
- setTimeout(run, 100);
-}, 100);</pre>
-
-<p>There is a difference between recursive <code>setTimeout()</code> and <code>setInterval()</code>:</p>
-
-<ul>
- <li>Recursive <code>setTimeout()</code> guarantees at least the specified amount of time (100ms in this example) will elapse between the executions; the code will run and then wait 100 milliseconds before it runs again. The interval will be the same regardless of how long the code takes to run.</li>
- <li>With <code>setInterval()</code>, the interval we choose <em>includes</em> the time taken to execute the code we want to run in. Let's say that the code takes 40 milliseconds to run — the interval then ends up being only 60 milliseconds.</li>
-</ul>
-
-<p>When your code has the potential to take longer to run than the time interval you’ve assigned, it’s better to use recursive <code>setTimeout()</code> — this will keep the time interval constant between executions regardless of how long the code takes to execute, and you won't get errors.</p>
-
-<h3 id="Browser_compatibility_2">Browser compatibility</h3>
-
-<p>{{Compat("api.WindowOrWorkerGlobalScope.setTimeout")}}</p>
-
-<h3 id="Further_information_2">Further information</h3>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a>, in particular <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals#settimeout()">setTimeout()</a></li>
- <li><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout() reference</a></li>
-</ul>
-
-<h2 id="setInterval">setInterval()</h2>
-
-<p><code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval()</a></code> is a method that allows you to run a function repeatedly with a set interval of time between each execution. Not as efficient as <code><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></code>, but allows you to choose a running rate/frame rate.</p>
-
-<table class="standard-table">
- <caption>Useful for...</caption>
- <thead>
- <tr>
- <th scope="col">Single delayed operation</th>
- <th scope="col">Repeating operation</th>
- <th scope="col">Multiple sequential operations</th>
- <th scope="col">Multiple simultaneous operations</th>
- </tr>
- <tr>
- <td>No</td>
- <td>Yes</td>
- <td>No (unless they are the same)</td>
- <td>No</td>
- </tr>
- </thead>
-</table>
-
-<h3 id="Code_example_3">Code example</h3>
-
-<p>The following function creates a new <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date">Date()</a></code> object, extracts a time string out of it using <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString">toLocaleTimeString()</a></code>, and then displays it in the UI. We then run it once per second using <code>setInterval()</code>, creating the effect of a digital clock that updates once per second (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">see this live</a>, and also <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">see the source</a>):</p>
-
-<pre class="brush: js">function displayTime() {
- let date = new Date();
- let time = date.toLocaleTimeString();
- document.getElementById('demo').textContent = time;
-}
-
-const createClock = setInterval(displayTime, 1000);</pre>
-
-<h3 id="Pitfalls_3">Pitfalls</h3>
-
-<ul>
- <li>The frame rate isn't optimized for the system the animation is running on, and can be somewhat inefficient. Unless you need to choose a specific (slower) framerate, it is generally better to use <code>requestAnimationFrame()</code>.</li>
-</ul>
-
-<h3 id="Browser_compatibility_3">Browser compatibility</h3>
-
-<p>{{Compat("api.WindowOrWorkerGlobalScope.setInterval")}}</p>
-
-<h3 id="Further_information_3">Further information</h3>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a>, in particular <a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval()</a></li>
- <li><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval() reference</a></li>
-</ul>
-
-<h2 id="requestAnimationFrame">requestAnimationFrame()</h2>
-
-<p><code><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></code> 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 <code>setInterval()</code>/recursive <code>setTimeout()</code>, unless you need a specific framerate.</p>
-
-<table class="standard-table">
- <caption>Useful for...</caption>
- <thead>
- <tr>
- <th scope="col">Single delayed operation</th>
- <th scope="col">Repeating operation</th>
- <th scope="col">Multiple sequential operations</th>
- <th scope="col">Multiple simultaneous operations</th>
- </tr>
- <tr>
- <td>No</td>
- <td>Yes</td>
- <td>No (unless it is the same one)</td>
- <td>No</td>
- </tr>
- </thead>
-</table>
-
-<h3 id="Code_example_4">Code example</h3>
-
-<p>A simple animated spinner; you can find this <a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/simple-raf-spinner.html">example live on GitHub</a> (see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/simple-raf-spinner.html">source code</a> also):</p>
-
-<pre class="brush: js">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 &gt; 359) {
- rotateCount %= 360;
- }
-
- spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
-
- rAF = requestAnimationFrame(draw);
-}
-
-draw();</pre>
-
-<h3 id="Pitfalls_4">Pitfalls</h3>
-
-<ul>
- <li>You can't choose a specific framerate with <code>requestAnimationFrame()</code>. If you need to run your animation at a slower framerate, you'll need to use <code>setInterval()</code> or recursive <code>setTimeout()</code>.</li>
-</ul>
-
-<h3 id="Browser_compatibility_4">Browser compatibility</h3>
-
-<p>{{Compat("api.Window.requestAnimationFrame")}}</p>
-
-<h3 id="Further_information_4">Further information</h3>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a>, in particular <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals#requestanimationframe()">requestAnimationFrame()</a></li>
- <li><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame() reference</a></li>
-</ul>
-
-<h2 id="Promises">Promises</h2>
-
-<p><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a> 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.</p>
-
-<table class="standard-table">
- <caption>Useful for...</caption>
- <thead>
- <tr>
- <th scope="col">Single delayed operation</th>
- <th scope="col">Repeating operation</th>
- <th scope="col">Multiple sequential operations</th>
- <th scope="col">Multiple simultaneous operations</th>
- </tr>
- <tr>
- <td>No</td>
- <td>No</td>
- <td>Yes</td>
- <td>See <code>Promise.all()</code>, below</td>
- </tr>
- </thead>
-</table>
-
-<h3 id="Code_example_5">Code example</h3>
-
-<p>The following code fetches an image from the server and displays it inside an {{htmlelement("img")}} element; <a href="https://mdn.github.io/learning-area/javascript/asynchronous/promises/simple-fetch-chained.html">see it live also</a>, and see also <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/simple-fetch-chained.html">the source code</a>:</p>
-
-<pre class="brush: js">fetch('coffee.jpg')
-.then(response =&gt; response.blob())
-.then(myBlob =&gt; {
- let objectURL = URL.createObjectURL(myBlob);
- let image = document.createElement('img');
- image.src = objectURL;
- document.body.appendChild(image);
-})
-.catch(e =&gt; {
- console.log('There has been a problem with your fetch operation: ' + e.message);
-});</pre>
-
-<h3 id="Pitfalls_5">Pitfalls</h3>
-
-<p>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:</p>
-
-<pre class="brush: js">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...</pre>
-
-<p>It is better to use the chaining power of promises to go with a flatter, easier to parse structure:</p>
-
-<pre class="brush: js">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);
-});</pre>
-
-<p>or even:</p>
-
-<pre class="brush: js">remotedb.allDocs(...)
-.then(resultOfAllDocs =&gt; {
- return localdb.put(...);
-})
-.then(resultOfPut =&gt; {
- return localdb.get(...);
-})
-.then(resultOfGet =&gt; {
- return localdb.put(...);
-})
-.catch(err =&gt; console.log(err));</pre>
-
-<p>That covers a lot of the basics. For a much more complete treatment, see the excellent <a href="https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html">We have a problem with promises</a>, by Nolan Lawson.</p>
-
-<h3 id="Browser_compatibility_5">Browser compatibility</h3>
-
-<p>{{Compat("javascript.builtins.Promise")}}</p>
-
-<h3 id="Further_information_5">Further information</h3>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">Graceful asynchronous programming with Promises</a></li>
- <li><a href="/en-US/docs/Web/JavaScript/Guide/Using_promises">Using promises</a></li>
- <li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise reference</a></li>
-</ul>
-
-<h2 id="Promise.all">Promise.all()</h2>
-
-<p>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.</p>
-
-<table class="standard-table">
- <caption>Useful for...</caption>
- <thead>
- <tr>
- <th scope="col">Single delayed operation</th>
- <th scope="col">Repeating operation</th>
- <th scope="col">Multiple sequential operations</th>
- <th scope="col">Multiple simultaneous operations</th>
- </tr>
- <tr>
- <td>No</td>
- <td>No</td>
- <td>No</td>
- <td>Yes</td>
- </tr>
- </thead>
-</table>
-
-<h3 id="Code_example_6">Code example</h3>
-
-<p>The following example fetches several resources from the server, and uses <code>Promise.all()</code> to wait for all of them to be available before then displaying all of them — <a href="https://mdn.github.io/learning-area/javascript/asynchronous/promises/promise-all.html">see it live</a>, and see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/promise-all.html">source code</a>:</p>
-
-<pre class="brush: js">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 =&gt; {
- // 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 =&gt; {
- 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 =&gt; {
- 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 &lt;img&gt; 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);
-});</pre>
-
-<h3 id="Pitfalls_6">Pitfalls</h3>
-
-<ul>
- <li>If a <code>Promise.all()</code> rejects, then one or more of the promises you are feeding into it inside its array parameter must be rejecting, or might not be returning promises at all. You need to check each one to see what they returned. </li>
-</ul>
-
-<h3 id="Browser_compatibility_6">Browser compatibility</h3>
-
-<p>{{Compat("javascript.builtins.Promise.all")}}</p>
-
-<h3 id="Further_information_6">Further information</h3>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises#running_code_in_response_to_multiple_promises_fulfilling">Running code in response to multiple promises fulfilling</a></li>
- <li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all">Promise.all() reference</a></li>
-</ul>
-
-<h2 id="Asyncawait">Async/await</h2>
-
-<p>Syntactic sugar built on top of promises that allows you to run asynchronous operations using syntax that's more like writing synchronous callback code.</p>
-
-<table class="standard-table">
- <caption>Useful for...</caption>
- <thead>
- <tr>
- <th scope="col">Single delayed operation</th>
- <th scope="col">Repeating operation</th>
- <th scope="col">Multiple sequential operations</th>
- <th scope="col">Multiple simultaneous operations</th>
- </tr>
- <tr>
- <td>No</td>
- <td>No</td>
- <td>Yes</td>
- <td>Yes (in combination with <code>Promise.all()</code>)</td>
- </tr>
- </thead>
-</table>
-
-<h3 id="Code_example_7">Code example</h3>
-
-<p>The following example is a refactor of the simple promise example we saw earlier that fetches and displays an image, written using async/await (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/simple-refactored-fetch.html">see it live</a>, and see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/simple-refactored-fetch.html">source code</a>):</p>
-
-<pre class="brush: js">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();</pre>
-
-<h3 id="Pitfalls_7">Pitfalls</h3>
-
-<ul>
- <li>You can't use the <code>await</code> operator inside a non-<code>async</code> function, or in the top level context of your code. This can sometimes result in an extra function wrapper needing to be created, which can be slightly frustrating in some circumstances. But it is worth it most of the time.</li>
- <li>Browser support for async/await is not as good as that for promises. If you want to use async/await but are concerned about older browser support, you could consider using the <a href="https://babeljs.io/">BabelJS</a> library — this allows you to write your applications using the latest JavaScript and let Babel figure out what changes if any are needed for your user’s browsers.</li>
-</ul>
-
-<h3 id="Browser_compatibility_7">Browser compatibility</h3>
-
-<p>{{Compat("javascript.statements.async_function")}}</p>
-
-<h3 id="Further_information_7">Further information</h3>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">Making asynchronous programming easier with async and await</a></li>
- <li><a href="/en-US/docs/Web/JavaScript/Reference/Statements/async_function">Async function reference</a></li>
- <li><a href="/en-US/docs/Web/JavaScript/Reference/Operators/await">Await operator reference</a></li>
-</ul>
-
-<p>{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}</p>
-
-<h2 id="In_this_module">In this module</h2>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">General asynchronous programming concepts</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">Introducing asynchronous JavaScript</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">Graceful asynchronous programming with Promises</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">Making asynchronous programming easier with async and await</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">Choosing the right approach</a></li>
-</ul>
diff --git a/files/zh-tw/learn/javascript/asynchronous/concepts/index.html b/files/zh-tw/learn/javascript/asynchronous/concepts/index.html
deleted file mode 100644
index 0db2f89275..0000000000
--- a/files/zh-tw/learn/javascript/asynchronous/concepts/index.html
+++ /dev/null
@@ -1,169 +0,0 @@
----
-title: 非同步程式設計通用概念
-slug: Learn/JavaScript/Asynchronous/Concepts
-tags:
- - JavaScript
- - Learn
- - Promises
- - Threads
- - asynchronous
- - blocking
- - 非同步
- - 執行緒
----
-<div>{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}}</div>
-
-<p>在本篇文章我們會介紹一些關於非同步程式設計的重要觀念,以及在網頁瀏覽器和 JavaScript 中的行為。在閱讀其他文章之前你應該先具備這些觀念。</p>
-
-<table class="learn-box standard-table">
- <tbody>
- <tr>
- <th scope="row">先備知識:</th>
- <td>基本的電腦概念,理解 JavaScript 的基本原理。</td>
- </tr>
- <tr>
- <th scope="row">學習目標:</th>
- <td>了解非同步程式設計背後的基本概念,以及其如何在網頁瀏覽器及 JavaScript 作呈現。</td>
- </tr>
- </tbody>
-</table>
-
-<h2 id="Asynchronous">非同步?</h2>
-
-<p>一般來說,在程式碼的執行順序當下同一時間只會處理一件事。如果某個函式依賴於其他函式的執行結果,那麼它就需要等待其完成並回傳結果才能執行,直到那樣的情況發生,以使用者的角度而言整個程式才是真正的結束。</p>
-
-<p>Mac 的使用者有時候會發現游標變成彩虹色的旋轉圖案(或經常被稱作為「沙灘球」)。這個游標的出現代表作業系統像是正對著你說:「你在執行的程式目前正在等待其他程式的完成而被暫停,因為花了一點時間所以我擔心你會想知道發生了甚麼事。」</p>
-
-<p><img alt="Multi-colored macOS beachball busy spinner" src="beachball.jpg" style="display: block; float: left; margin: 0px 30px 0px 0px;"></p>
-
-<br/>
-
-<p>這種經驗通常令人沮喪而且這也並未善加利用電腦處理器的能力——特別是在擁有多核處理器的世代。比起只能毫無意義的坐著乾等,如果能夠安排其他任務交由其他處理器來執行並讓你知道任務何時完成,這樣就有效率多了,像這樣能讓其他工作在同時間完成,這就是<strong>非同步程式設計</strong>的基礎。這取決於你目前正在使用的程式環境,如同網頁瀏覽器會提供一些 API 能讓你非同步的執行任務。</p>
-
-<br/><br/>
-
-<h2 id="Blocking_code">阻塞程式碼</h2>
-
-<p>非同步的技巧非常實用,特別是在網頁程式設計上。當一個網頁應用程式運行在瀏覽器且執行大量的程式碼的當下並未將控制權交還給瀏覽器時,瀏覽器可能會處於凍結狀態。我們稱之為<strong>阻塞( blocking )</strong>,瀏覽器正在持續處理使用者的輸入並執行其他任務就會被阻塞住,直到應用程式將處理器的控制權歸還後才會解除。</p>
-
-<p>我們來看幾個程式範例來說明阻塞是甚麼意思。</p>
-
-<p>在這個例子 <a href="https://github.com/mdn/learning-area/tree/master/javascript/asynchronous/introducing/simple-sync.html">simple-sync.html</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync.html">線上範例</a>),我們在按鈕增加一個點擊事件監聽器,在點擊時會執行一段相當耗時的操作(執行新增日期物件一千萬次並將最後一次的結果顯示在控制台上)然後再加入一則文字段落到 {{Glossary("DOM")}} 上:</p>
-
-<pre class="brush: js">const btn = document.querySelector('button');
-btn.addEventListener('click', () =&gt; {
- let myDate;
- for(let i = 0; i &lt; 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);
-});</pre>
-
-<p>當執行我們的範例時,打開你的 JavaScript 控制台並點擊按鈕——你會注意到文字並不會馬上就出現在畫面上,而是要等到跑完新增日期物件的迴圈並將結果顯示在控制台上後,新增的文字才會出現。原因在於程式碼依照順序執行,因此某一筆的操作必須等待上一筆執行完成後才會執行。</p>
-
-<div class="notecard note">
-<p><strong>注意</strong>:上述的範例只是用來作為了解原理的基本範例,在現實生活中你不會真的執行一千萬次的新增日期物件。</p>
-</div>
-
-<p>在我們第二個範例, <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-ui-blocking.html">simple-sync-ui-blocking.html</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-ui-blocking.html">線上範例</a>),我們模擬了一段比較貼近現實的範例,你或許會在現實的頁面上遇到。我們在渲染畫面的期間阻擋和使用者的互動行為。在本範例,我們有兩個按鈕:</p>
-
-<ul>
- <li>當「 Fill canvas 」按鈕被點擊時,我們會填滿一百萬個藍色圓圈到可用的 {{htmlelement("canvas")}} 標籤內。</li>
- <li>當點擊「 Click me for alert 」按鈕後會顯示警告訊息。</li>
-</ul>
-
-<pre class="brush: js">function expensiveOperation() {
- for(let i = 0; i &lt; 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', () =&gt;
- alert('You clicked me!')
-);</pre>
-
-<p>當點擊第一個按鈕之後緊接著點擊第二個按鈕,你會發現警告訊息並不會馬上出現,而是要等到圓圈圖案被渲染完成後才會出現警告訊息視窗。第一個操作在完成之前都會阻擋第二個操作的執行。</p>
-
-<div class="notecard note">
-<p><strong>注意</strong>:我們是在偽造一個阻塞且這個例子可能有點醜陋,但這卻是在現實中大多數的開發者試圖避免的常見問題。</p>
-</div>
-
-<p>為什麼會這樣?事實是因為 JavaScript 在執行上是使用 <strong>單一執行緒( single-threaded )</strong>。現在,我們需要說明一下<strong>執行緒( thread )</strong>的概念。</p>
-
-<h2 id="Threads">執行緒</h2>
-
-<p><strong>執行緒( thread )</strong>基本上是可以用來完成程式碼任務的單一行程( process )。每一條執行緒在同一時間只會執行一項任務:</p>
-
-<pre>任務 A --&gt; 任務 B --&gt; 任務 C</pre>
-
-<p>每一項任務必須依照順序來執行,下一項任務必須要等到前一項任務完成後才能開始執行。</p>
-
-<p>我們稍早所說,現今許多電腦擁有許多顆核心,所以可以在同一時間做許多事。可以支援多條執行緒的程式語言在同一時間可以利用多顆核心來完成數個任務:</p>
-
-<pre>執行緒1:任務 A --&gt; 任務 B
-執行緒2:任務 C --&gt; 任務 D</pre>
-
-<h3 id="JavaScript_is_single-threaded">JavaScript 是單執行緒的</h3>
-
-<p>JavaScript 在傳統意義上是跑在一條單執行緒。即便你的電腦有多顆核心,也只能在 JavaScript 上面跑一條執行緒來完成任務,這一條執行緒我們稱為<strong>主執行緒( main thread )</strong>。我們稍早的第二個例子執行流程就像是底下這樣:</p>
-
-<pre>主執行緒:渲染圓圈圖形到 canvas --&gt; 顯示 alert()</pre>
-
-<p>在經過一段時間的發展後, JavaScript 取得某種工具來處理上述的問題。 <a href="/zh-TW/docs/Web/API/Web_Workers_API">Web worker</a> 允許傳送一些任務到不同的執行緒上,因此呼叫一個 worker 你可以在同一時間跑不同的任務區塊。透過使用 worker 就可以和主執行緒分工合作,因此不會阻塞和使用者的互動。</p>
-
-<pre> 主執行緒:任務 A --&gt; 任務 C
-背景工作執行緒:耗時的任務 B</pre>
-
-<p>依照這個想法,我們來改寫之前的例子 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-worker.html">simple-sync-worker.html</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-worker.html">線上範例</a>),一樣打開你的 JavaScript 控制台吧。我們改寫利用 worker 產生額外的執行緒來執行一千萬次的呼叫新增日期物件的範例。現在你點擊一下按鈕,你會發現新增的文字段落馬上就會顯示在畫面上,而稍後日期才會顯示在 JavaScript 控制台。第一項操作不再阻塞第二項的執行了。</p>
-
-<h2 id="Asynchronous_code">非同步的程式碼</h2>
-
-<p><a href="/zh-TW/docs/Web/API/Web_Workers_API">Web worker</a> 相當有用,但還是有它的限制在。最主要的限制是在它不能直接存取 {{Glossary("DOM")}} ——你不能透過 worker 去直接更新你的 UI 。我們不能透過 worker 去渲染一百萬個藍色圈圈,它基本上只能做一些數字運算的工作。</p>
-
-<p>第二個問題是即使我們的程式跑在 worker 上不會阻塞主執行緒的執行,但基本上我們還是處於同步的。當一個函式的執行依賴於多個函式的執行結果就會發生問題。看看底下的執行緒圖表:</p>
-
-<pre>主執行緒:任務 A --&gt; 任務 B</pre>
-
-<p>在這個例子,任務 A 正在做一些像是從遠端伺服器抓取圖片的工作,任務 B 會將抓取下來的圖片套用一些濾鏡的特效。如果你執行任務 A 後立即執行任務 B ,你會收到錯誤訊息,因為任務 B 當下並沒有圖片可以套用。</p>
-
-<pre> 主執行緒:任務 A --&gt; 任務 B --&gt; |任務 D |
-背景工作執行緒:任務 C -------------&gt; | |</pre>
-
-<p>在這個例子,我們說任務 D 必須依賴任務 B 及任務 C 的執行結果。如果我們保證在執行任務 D 的當下都已經取得 B 和 C 的執行結果,這也許沒甚麼問題,但這不太可能。任務 D 執行的當下若有任一個依賴結果尚未完成,勢必會發生錯誤。</p>
-
-<p>為了修正此問題,瀏覽器允許我們非同步的運行某些操作。如 <a href="/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a> 功能允許你在一項正在執行的操作做設定(例如:從遠端伺服器抓取圖片),然後等待其執行結果回傳之後再進行之後的操作:</p>
-
-<pre>主執行緒:任務 A 任務 B
-Promise : |_____非同步操作_____|</pre>
-
-<p>因為操作發生在其他地方,因此主執行緒並不會在執行非同步的工作時被阻塞住。</p>
-
-<p>我們會在下一篇文章來看看我們如何寫出非同步的程式碼,真令人興奮,對吧?讓我們繼續看下去吧!</p>
-
-<h2 id="Conclusion">結論</h2>
-
-<p>現代軟體越來越多圍繞在使用非同步的程式技巧來設計,讓程式碼可以在同一時間做越多事。當你使用目前越新越強大的 API 時,你會發現更多情況之下他們處理工作唯一的方法就是使用非同步的處理方式。過去我們很難寫出可以執行非同步的程式碼,但現在簡單多了。在剩餘的單元中,我們將會進一步的探討為何非同步如此重要,以及如何設計出避免上述某些問題的程式碼。</p>
-
-<div>{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}}</div>
-
-<h2 id="In_this_module">在本單元</h2>
-
-<ul>
- <li><a href="/zh-TW/docs/Learn/JavaScript/Asynchronous/Concepts">非同步程式設計通用概念</a></li>
- <li><a href="/zh-TW/docs/Learn/JavaScript/Asynchronous/Introducing">非同步的 JavaScript 介紹</a></li>
- <li><a href="/zh-TW/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">協同的非同步 JavaScript : Timeout 和 interval</a></li>
- <li><a href="/zh-TW/docs/Learn/JavaScript/Asynchronous/Promises">優雅的使用 Promise 來處理非同步操作</a></li>
- <li><a href="/zh-TW/docs/Learn/JavaScript/Asynchronous/Async_await">利用 async 及 await 讓非同步程式設計變得更容易</a></li>
- <li><a href="/zh-TW/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">選擇正確的方法</a></li>
-</ul>
diff --git a/files/zh-tw/learn/javascript/asynchronous/timeouts_and_intervals/index.html b/files/zh-tw/learn/javascript/asynchronous/timeouts_and_intervals/index.html
deleted file mode 100644
index 9e076bcad4..0000000000
--- a/files/zh-tw/learn/javascript/asynchronous/timeouts_and_intervals/index.html
+++ /dev/null
@@ -1,646 +0,0 @@
----
-title: '協同的非同步 JavaScript: Timeouts 和 intervals'
-slug: Learn/JavaScript/Asynchronous/Timeouts_and_intervals
-tags:
- - Animation
- - Beginner
- - CodingScripting
- - Guide
- - Intervals
- - JavaScript
- - Loops
- - asynchronous
- - requestAnimationFrame
- - setInterval
- - setTimeout
- - timeouts
----
-<div>{{LearnSidebar}}</div>
-
-<div>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}</div>
-
-<p class="summary">在這裡看看我們在傳統上是如何透過設定的時間到期或是透過定時的方式 (如每秒發生的次數) 讓 Javascript 能夠以非同步的方式執行程式碼,談談這些方法有哪些用處以及存在哪些既有的問題。</p>
-
-<table class="learn-box standard-table">
- <tbody>
- <tr>
- <th scope="row">Prerequisites:</th>
- <td>Basic computer literacy, a reasonable understanding of JavaScript fundamentals.</td>
- </tr>
- <tr>
- <th scope="row">Objective:</th>
- <td>To understand asynchronous loops and intervals and what they are useful for.</td>
- </tr>
- </tbody>
-</table>
-
-<h2 id="Introduction">Introduction</h2>
-
-<p>For a long time, the web platform has offered JavaScript programmers a number of functions that allow them to asynchronously execute code after a certain time interval has elapsed, and to repeatedly execute a block of code asynchronously until you tell it to stop.</p>
-
-<p>These functions are:</p>
-
-<dl>
- <dt><code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout()</a></code></dt>
- <dd>Execute a specified block of code once after a specified time has elapsed.</dd>
- <dt><code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval()</a></code></dt>
- <dd>Execute a specified block of code repeatedly with a fixed time delay between each call.</dd>
- <dt><code><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></code></dt>
- <dd>The modern version of <code>setInterval()</code>. Executes a specified block of code before the browser next repaints the display, allowing an animation to be run at a suitable framerate regardless of the environment it is being run in.</dd>
-</dl>
-
-<p>The asynchronous code set up by these functions runs on the main thread (after their specified timer has elapsed).</p>
-
-<p>It's important to know that you can (and often will) run other code before a <code>setTimeout()</code> call executes, or between iterations of <code>setInterval()</code>. Depending on how processor-intensive these operations are, they can delay your async code even further, as any async code will execute only <em>after</em> the main thread is available. (In other words, when the stack is empty.)  You will learn more on this matter as you progress through this article.</p>
-
-<p>In any case, these functions are used for running constant animations and other background processing on a web site or application. In the following sections we will show you how they can be used.</p>
-
-<h2 id="setTimeout">setTimeout()</h2>
-
-<p>As we said before, <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout()</a></code> executes a particular block of code once after a specified time has elapsed. It takes the following parameters:</p>
-
-<ul>
- <li>A function to run, or a reference to a function defined elsewhere.</li>
- <li>A number representing the time interval in milliseconds (1000 milliseconds equals 1 second) to wait before executing the code. If you specify a value of <code>0</code> (or omit the value), the function will run as soon as possible. (See the note below on why it runs "as soon as possible" and not "immediately".) More on why you might want to do this later.</li>
- <li>Zero or more values that represent any parameters you want to pass to the function when it is run.</li>
-</ul>
-
-<div class="notecard note">
-<p><strong>NOTE:</strong> The specified amount of time (or the delay) is <strong>not</strong> the <em>guaranteed time</em> to execution, but rather the <em>minimum time</em> to execution. The callbacks you pass to these functions cannot run until the stack on the main thread is empty.</p>
-
-<p>As a consequence, code like <code>setTimeout(fn, 0)</code> will execute as soon as the stack is empty, <strong>not</strong> immediately. If you execute code like <code>setTimeout(fn, 0)</code> but then immediately after run a loop that counts from 1 to 10 billion, your callback will be executed after a few seconds.</p>
-</div>
-
-<p>In the following example, the browser will wait two seconds before executing the anonymous function, then will display the alert message (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/simple-settimeout.html">see it running live</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/simple-settimeout.html">see the source code</a>):</p>
-
-<pre class="brush: js">let myGreeting = setTimeout(function() {
- alert('Hello, Mr. Universe!');
-}, 2000)</pre>
-
-<p>The functions you specify don't have to be anonymous. You can give your function a name, and even define it somewhere else and pass a function reference to the <code>setTimeout()</code>. The following two versions of the code snippet are equivalent to the first one:</p>
-
-<pre class="brush: js">// 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);</pre>
-
-<p>That can be useful if you have a function that needs to be called both from a timeout and in response to an event, for example. But it can also just help keep your code tidy, especially if the timeout callback is more than a few lines of code.</p>
-
-<p><code>setTimeout()</code> returns an identifier value that can be used to refer to the timeout later, such as when you want to stop it. See {{anch("Clearing timeouts")}} (below) to learn how to do that.</p>
-
-<h3 id="Passing_parameters_to_a_setTimeout_function">Passing parameters to a setTimeout() function</h3>
-
-<p>Any parameters that you want to pass to the function being run inside the <code>setTimeout()</code> must be passed to it as additional parameters at the end of the list.</p>
-
-<p>For example, you could refactor the previous function so that it will say hi to whatever person's name is passed to it:</p>
-
-<pre class="brush: js">function sayHi(who) {
- alert(`Hello ${who}!`);
-}</pre>
-
-<p>Now, you can pass the name of the person into the <code>setTimeout()</code> call as a third parameter:</p>
-
-<pre class="brush: js">let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');</pre>
-
-<h3 id="Clearing_timeouts">Clearing timeouts</h3>
-
-<p>Finally, if a timeout has been created, you can cancel it before the specified time has elapsed by calling <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout">clearTimeout()</a></code>, passing it the identifier of the <code>setTimeout()</code> call as a parameter. So to cancel our above timeout, you'd do this:</p>
-
-<pre class="brush: js">clearTimeout(myGreeting);</pre>
-
-<div class="notecard note">
-<p><strong>Note</strong>: See <code><a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/greeter-app.html">greeter-app.html</a></code> for a slightly more involved demo that allows you to set the name of the person to say hello to in a form, and cancel the greeting using a separate button (<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/greeter-app.html">see the source code also</a>).</p>
-</div>
-
-<h2 id="setInterval">setInterval()</h2>
-
-<p><code>setTimeout()</code> works perfectly when you need to run code once after a set period of time. But what happens when you need to run the code over and over again—for example, in the case of an animation?</p>
-
-<p>This is where <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval()</a></code> comes in. This works in a very similar way to <code>setTimeout()</code>, except that the function you pass as the first parameter is executed repeatedly at no less than the number of milliseconds given by the second parameter apart, rather than once. You can also pass any parameters required by the function being executed as subsequent parameters of the <code>setInterval()</code> call.</p>
-
-<p>Let's look at an example. The following function creates a new <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date">Date()</a></code> object, extracts a time string out of it using <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString">toLocaleTimeString()</a></code>, and then displays it in the UI. It then runs the function once per second using <code>setInterval()</code>, creating the effect of a digital clock that updates once per second (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">see this live</a>, and also <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">see the source</a>):</p>
-
-<pre class="brush: js">function displayTime() {
- let date = new Date();
- let time = date.toLocaleTimeString();
- document.getElementById('demo').textContent = time;
-}
-
-const createClock = setInterval(displayTime, 1000);</pre>
-
-<p>Just like <code>setTimeout()</code>, <code>setInterval()</code> returns an identifying value you can use later when you need to clear the interval.</p>
-
-<h3 id="Clearing_intervals">Clearing intervals</h3>
-
-<p><code>setInterval()</code> keeps running a task forever, unless you do something about it. You'll probably want a way to stop such tasks, otherwise you may end up getting errors when the browser can't complete any further versions of the task, or if the animation being handled by the task has finished. You can do this the same way you stop timeouts — by passing the identifier returned by the <code>setInterval()</code> call to the <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearInterval">clearInterval()</a></code> function:</p>
-
-<pre class="brush: js">const myInterval = setInterval(myFunction, 2000);
-
-clearInterval(myInterval);</pre>
-
-<h4 id="Active_learning_Creating_your_own_stopwatch!">Active learning: Creating your own stopwatch!</h4>
-
-<p>With this all said, we've got a challenge for you. Take a copy of our <code><a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">setInterval-clock.html</a></code> example, and modify it to create your own simple stopwatch.</p>
-
-<p>You need to display a time as before, but in this example, you need:</p>
-
-<ul>
- <li>A "Start" button to start the stopwatch running.</li>
- <li>A "Stop" button to pause/stop it.</li>
- <li>A "Reset" button to reset the time back to <code>0</code>.</li>
- <li>The time display to show the number of seconds elapsed, rather than the actual time.</li>
-</ul>
-
-<p>Here's a few hints for you:</p>
-
-<ul>
- <li>You can structure and style the button markup however you like; just make sure you use semantic HTML, with hooks to allow you to grab the button references using JavaScript.</li>
- <li>You probably want to create a variable that starts at <code>0</code>, then increments by one every second using a constant loop.</li>
- <li>It is easier to create this example without using a <code>Date()</code> object, like we've done in our version, but less accurate — you can't guarantee that the callback will fire after exactly <code>1000</code>ms. A more accurate way would be to run <code>startTime = Date.now()</code> to get a timestamp of exactly when the user clicked the start button, and then do <code>Date.now() - startTime</code> to get the number of milliseconds after the start button was clicked.</li>
- <li>You also want to calculate the number of hours, minutes, and seconds as separate values, and then show them together in a string after each loop iteration. From the second counter, you can work out each of these.</li>
- <li>How would you calculate them? Have a think about it:
- <ul>
- <li>The number of seconds in an hour is <code>3600</code>.</li>
- <li>The number of minutes will be the amount of seconds left over when all of the hours have been removed, divided by <code>60</code>.</li>
- <li>The number of seconds will be the amount of seconds left over when all of the minutes have been removed.</li>
- </ul>
- </li>
- <li>You'll want to include a leading zero on your display values if the amount is less than <code>10</code>, so it looks more like a traditional clock/watch.</li>
- <li>To pause the stopwatch, you'll want to clear the interval. To reset it, you'll want to set the counter back to <code>0</code>, clear the interval, and then immediately update the display.</li>
- <li>You probably ought to disable the start button after pressing it once, and enable it again after you've stopped/reset it. Otherwise multiple presses of the start button will apply multiple <code>setInterval()</code>s to the clock, leading to wrong behavior.</li>
-</ul>
-
-<div class="notecard note">
-<p><strong>Note</strong>: If you get stuck, you can <a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/setinterval-stopwatch.html">find our version here</a> (see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/setinterval-stopwatch.html">source code</a> also).</p>
-</div>
-
-<h2 id="Things_to_keep_in_mind_about_setTimeout_and_setInterval">Things to keep in mind about setTimeout() and setInterval()</h2>
-
-<p>There are a few things to keep in mind when working with <code>setTimeout()</code> and <code>setInterval()</code>. Let's review these now.</p>
-
-<h3 id="Recursive_timeouts">Recursive timeouts</h3>
-
-<p>There is another way to use <code>setTimeout()</code>: you can call it recursively to run the same code repeatedly, instead of using <code>setInterval()</code>.</p>
-
-<p>The below example uses a recursive <code>setTimeout()</code> to run the passed function every <code>100</code> milliseconds:</p>
-
-<pre class="brush: js">let i = 1;
-
-setTimeout(function run() {
- console.log(i);
- i++;
- setTimeout(run, 100);
-}, 100);</pre>
-
-<p>Compare the above example to the following one — this uses <code>setInterval()</code> to accomplish the same effect:</p>
-
-<pre class="brush: js">let i = 1;
-
-setInterval(function run() {
- console.log(i);
- i++
-}, 100);</pre>
-
-<h4 id="How_do_recursive_setTimeout_and_setInterval_differ">How do recursive <code>setTimeout()</code> and <code>setInterval()</code> differ?</h4>
-
-<p>The difference between the two versions of the above code is a subtle one.</p>
-
-<ul>
- <li>Recursive <code>setTimeout()</code> guarantees the given delay between the code execution completion and the next call. The delay for the next execution will start counting only after the code has finished running, therefore <em>excluding</em> the time taken to run the code. In this example, the <code>100</code> milliseconds will be the delay between the <code>run</code> code finishing, and the next <code>run</code> call.</li>
- <li>The example using <code>setInterval()</code> does things somewhat differently. The interval you chose <em>includes</em> the time taken to execute the code you want to run in. Let's say that the code takes <code>40</code> milliseconds to run — the interval then ends up being only <code>60</code> milliseconds.</li>
- <li>When using <code>setTimeout()</code> recursively, each iteration can calculate a different delay before running the next iteration. In other words, the value of the second parameter can specify a different time in milliseconds to wait before running the code again.</li>
-</ul>
-
-<p>When your code has the potential to take longer to run than the time interval you’ve assigned, it’s better to use recursive <code>setTimeout()</code> — this will keep the time interval constant between executions regardless of how long the code takes to execute, and you won't get errors.</p>
-
-<h3 id="Immediate_timeouts">Immediate timeouts</h3>
-
-<p>Using <code>0</code> as the value for <code>setTimeout()</code> schedules the execution of the specified callback function as soon as possible but only after the main code thread has been run.</p>
-
-<p>For instance, the code below (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/zero-settimeout.html">see it live</a>) outputs an alert containing <code>"Hello"</code>, then an alert containing <code>"World"</code> as soon as you click OK on the first alert.</p>
-
-<pre class="brush: js">setTimeout(function() {
- alert('World');
-}, 0);
-
-alert('Hello');</pre>
-
-<p>This can be useful in cases where you want to set a block of code to run as soon as all of the main thread has finished running — put it on the async event loop, so it will run straight afterwards.</p>
-
-<h3 id="Clearing_with_clearTimeout_or_clearInterval">Clearing with clearTimeout() or clearInterval()</h3>
-
-<p><code>clearTimeout()</code> and <code>clearInterval()</code> both use the same list of entries to clear from. Interestingly enough, this means that you can use either method to clear a <code>setTimeout()</code> or <code>setInterval()</code>.</p>
-
-<p>For consistency, you should use <code>clearTimeout()</code> to clear <code>setTimeout()</code> entries and <code>clearInterval()</code> to clear <code>setInterval()</code> entries. This will help to avoid confusion.</p>
-
-<h2 id="requestAnimationFrame">requestAnimationFrame()</h2>
-
-<p><code><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></code> is a specialized looping function created for running animations efficiently in the browser. It is basically the modern version of <code>setInterval()</code> — it executes a specified block of code before the browser next repaints the display, allowing an animation to be run at a suitable frame rate regardless of the environment it is being run in.</p>
-
-<p>It was created in response to perceived problems with <code>setInterval()</code>, which for example doesn't run at a frame rate optimized for the device, sometimes drops frames, continues to run even if the tab is not the active tab or the animation is scrolled off the page, etc.</p>
-
-<p>(<a href="http://creativejs.com/resources/requestanimationframe/index.html">Read more about this on CreativeJS</a>.)</p>
-
-<div class="notecard note">
-<p><strong>Note</strong>: You can find examples of using <code>requestAnimationFrame()</code> elsewhere in the course — see for example <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Drawing graphics</a>, and <a href="/en-US/docs/Learn/JavaScript/Objects/Object_building_practice">Object building practice</a>.</p>
-</div>
-
-<p>The method takes as an argument a callback to be invoked before the repaint. This is the general pattern you'll see it used in:</p>
-
-<pre class="brush: js">function draw() {
- // Drawing code goes here
- requestAnimationFrame(draw);
-}
-
-draw();</pre>
-
-<p>The idea is to define a function in which your animation is updated (e.g. your sprites are moved, score is updated, data is refreshed, or whatever). Then, you call it to start the process off. At the end of the function block you call <code>requestAnimationFrame()</code> with the function reference passed as the parameter, and this instructs the browser to call the function again on the next display repaint. This is then run continuously, as the code is calling <code>requestAnimationFrame()</code> recursively.</p>
-
-<div class="notecard note">
-<p><strong>Note</strong>: If you want to perform some kind of simple constant DOM animation, <a href="/en-US/docs/Web/CSS/CSS_Animations">CSS Animations</a> are probably faster. They are calculated directly by the browser's internal code, rather than JavaScript.</p>
-
-<p>If, however, you are doing something more complex and involving objects that are not directly accessible inside the DOM (such as <a href="/en-US/docs/Web/API/Canvas_API">2D Canvas API</a> or <a href="/en-US/docs/Web/API/WebGL_API">WebGL</a> objects), <code>requestAnimationFrame()</code> is the better option in most cases.</p>
-</div>
-
-<h3 id="How_fast_does_your_animation_run">How fast does your animation run?</h3>
-
-<p>The smoothness of your animation is directly dependent on your animation's frame rate and it is measured in frames per second (fps). The higher this number is, the smoother your animation will look, to a point.</p>
-
-<p>Since most screens have a refresh rate of 60Hz, the fastest frame rate you can aim for is 60 frames per second (FPS) when working with web browsers. However, more frames means more processing, which can often cause stuttering and skipping — also known as <em>dropping frames</em>, or <em>jank</em>.</p>
-
-<p>If you have a monitor with a 60Hz refresh rate and you want to achieve 60 FPS you have about 16.7 milliseconds (<code>1000 / 60</code>) to execute your animation code to render each frame. This is a reminder that you'll need to be mindful of the amount of code that you try to run during each pass through the animation loop.</p>
-
-<p><code>requestAnimationFrame()</code> always tries to get as close to this magic 60 FPS value as possible. Sometimes, it isn't possible — if you have a really complex animation and you are running it on a slow computer, your frame rate will be less. In all cases, <code>requestAnimationFrame()</code> will always do the best it can with what it has available.</p>
-
-<h3 id="How_does_requestAnimationFrame_differ_from_setInterval_and_setTimeout">How does requestAnimationFrame() differ from setInterval() and setTimeout()?</h3>
-
-<p>Let's talk a little bit more about how the <code>requestAnimationFrame()</code> method differs from the other methods used earlier. Looking at our code from above:</p>
-
-<pre class="brush: js">function draw() {
- // Drawing code goes here
- requestAnimationFrame(draw);
-}
-
-draw();</pre>
-
-<p>Let's now see how to do the same thing using <code>setInterval()</code>:</p>
-
-<pre class="brush: js">function draw() {
- // Drawing code goes here
-}
-
-setInterval(draw, 17);</pre>
-
-<p>As we covered earlier, you don't specify a time interval for <code>requestAnimationFrame()</code>. It just runs it as quickly and smoothly as possible in the current conditions. The browser also doesn't waste time running it if the animation is offscreen for some reason, etc.</p>
-
-<p><code>setInterval()</code>, on the other hand <em>requires</em> an interval to be specified. We arrived at our final value of 17 via the formula <em>1000 milliseconds / 60Hz</em>, and then rounded it up. Rounding up is a good idea; if you rounded down, the browser might try to run the animation faster than 60 FPS, and it wouldn't make any difference to the animation's smoothness, anyway. As we said before, 60Hz is the standard refresh rate.</p>
-
-<h3 id="Including_a_timestamp">Including a timestamp</h3>
-
-<p>The actual callback passed to the <code>requestAnimationFrame()</code> function can be given a parameter, too: a <em>timestamp</em> value, that represents the time since the <code>requestAnimationFrame()</code> started running.</p>
-
-<p>This is useful as it allows you to run things at specific times and at a constant pace, regardless of how fast or slow your device might be. The general pattern you'd use looks something like this:</p>
-
-<pre class="brush: js">let startTime = null;
-
-function draw(timestamp) {
- if (!startTime) {
- startTime = timestamp;
- }
-
- currentTime = timestamp - startTime;
-
- // Do something based on current time
-
- requestAnimationFrame(draw);
-}
-
-draw();</pre>
-
-<h3 id="Browser_support">Browser support</h3>
-
-<p><code>requestAnimationFrame()</code> is supported in more recent browsers than <code>setInterval()</code>/<code>setTimeout()</code>.  Interestingly, it is available in Internet Explorer 10 and above.</p>
-
-<p>So, unless you need to support older versions of IE, there is little reason to not use <code>requestAnimationFrame()</code>.</p>
-
-<h3 id="A_simple_example">A simple example</h3>
-
-<p>Enough with the theory! Let's build your own personal <code>requestAnimationFrame()</code> 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.</p>
-
-<div class="notecard note">
-<p><strong>Note</strong>: 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 <code>requestAnimationFrame()</code> 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.</p>
-</div>
-
-<ol>
- <li>
- <p>Grab a basic HTML template (<a href="https://github.com/mdn/learning-area/blob/master/html/introduction-to-html/getting-started/index.html">such as this one</a>).</p>
- </li>
- <li>
- <p>Put an empty {{htmlelement("div")}} element inside the {{htmlelement("body")}}, then add a ↻ character inside it. This circular arrow character will act as our spinner for this example.</p>
- </li>
- <li>
- <p>Apply the following CSS to the HTML template (in whatever way you prefer). This sets a red background on the page, sets the <code>&lt;body&gt;</code> height to <code>100%</code> of the {{htmlelement("html")}} height, and centers the <code>&lt;div&gt;</code> inside the <code>&lt;body&gt;</code>, horizontally and vertically.</p>
-
- <pre class="brush: css">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;
-}</pre>
- </li>
- <li>
- <p>Insert a {{htmlelement("script")}} element just above the closing <code>&lt;/body&gt;</code> tag.</p>
- </li>
- <li>
- <p>Insert the following JavaScript inside your <code>&lt;script&gt;</code> element. Here, you're storing a reference to the <code>&lt;div&gt;</code> inside a constant, setting a <code>rotateCount</code> variable to <code>0</code>, setting an uninitialized variable that will later be used to contain a reference to the <code>requestAnimationFrame()</code> call, and setting a <code>startTime</code> variable to <code>null</code>, which will later be used to store the start time of the <code>requestAnimationFrame()</code>.</p>
-
- <pre class="brush: js">const spinner = document.querySelector('div');
-let rotateCount = 0;
-let startTime = null;
-let rAF;
-</pre>
- </li>
- <li>
- <p>Below the previous code, insert a <code>draw()</code> function that will be used to contain our animation code, which includes the <code>timestamp</code> parameter:</p>
-
- <pre class="brush: js">function draw(timestamp) {
-
-}</pre>
- </li>
- <li>
- <p>Inside <code>draw()</code>, 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 <code>rotateCount</code> 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):</p>
-
- <pre class="brush: js"> if (!startTime) {
- startTime = timestamp;
- }
-
- rotateCount = (timestamp - startTime) / 3;
-</pre>
- </li>
- <li>
- <p>Below the previous line inside <code>draw()</code>, add the following block — this ensures that the value of <code>rotateCount</code> is between <code>0</code> and <code>359</code>, by setting the value to its modulo of <code>360</code> (i.e. the remainder left over when the value is divided by <code>360</code>) — 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 <code>0</code>–<code>359</code> degrees than values like <code>"128000 degrees"</code>.</p>
-
- <pre class="brush: js"> rotateCount %= 360; </pre>
- </li>
- <li>Next, below the previous block add the following line to actually rotate the spinner:
- <pre class="brush: js">spinner.style.transform = `rotate(${rotateCount}deg)`;</pre>
- </li>
- <li>
- <p>At the very bottom inside the <code>draw()</code> function, insert the following line. This is the key to the whole operation — you are setting the variable defined earlier to an active <code>requestAnimation()</code> call, which takes the <code>draw()</code> function as its parameter. This starts the animation off, constantly running the <code>draw()</code> function at a rate as near 60 FPS as possible.</p>
-
- <pre class="brush: js">rAF = requestAnimationFrame(draw);</pre>
- </li>
- <li>
- <p>Below the <code>draw()</code> function definition, add a call to the <code>draw()</code> function to start the animation.</p>
-
- <pre class="brush: js">draw();</pre>
- </li>
-</ol>
-
-<div class="notecard note">
-<p><strong>Note</strong>: You can find the finished <a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/simple-raf-spinner.html">example live on GitHub</a>. (You can see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/simple-raf-spinner.html">source code</a>, also.)</p>
-</div>
-
-<h3 id="Clearing_a_requestAnimationFrame_call">Clearing a requestAnimationFrame() call</h3>
-
-<p>Clearing a <code>requestAnimationFrame()</code> call can be done by calling the corresponding <code>cancelAnimationFrame()</code> method. (Note that the function name starts with "cancel", not "clear" as with the "set..." methods.) </p>
-
-<p>Just pass it the value returned by the <code>requestAnimationFrame()</code> call to cancel, which you stored in the variable <code>rAF</code>:</p>
-
-<pre class="brush: js">cancelAnimationFrame(rAF);</pre>
-
-<h3 id="Active_learning_Starting_and_stopping_our_spinner">Active learning: Starting and stopping our spinner</h3>
-
-<p>In this exercise, we'd like you to test out the <code>cancelAnimationFrame()</code> 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.</p>
-
-<p>Some hints:</p>
-
-<ul>
- <li>A <code>click</code> event handler can be added to most elements, including the document <code>&lt;body&gt;</code>. It makes more sense to put it on the <code>&lt;body&gt;</code> element if you want to maximize the clickable area — the event bubbles up to its child elements.</li>
- <li>You'll want to add a tracking variable to check whether the spinner is spinning or not, clearing the animation frame if it is, and calling it again if it isn't.</li>
-</ul>
-
-<div class="notecard note">
-<p><strong>Note</strong>: Try this yourself first; if you get really stuck, check out of our <a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/start-and-stop-spinner.html">live example</a> and <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/start-and-stop-spinner.html">source code</a>.</p>
-</div>
-
-<h3 id="Throttling_a_requestAnimationFrame_animation">Throttling a requestAnimationFrame() animation</h3>
-
-<p>One limitation of <code>requestAnimationFrame()</code> 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?</p>
-
-<p>This was a problem, for example, in the Monkey Island-inspired walking animation from our <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Drawing Graphics</a> article:</p>
-
-<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation.html", '100%', 260)}}</p>
-
-<p>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 <code>requestAnimationFrame()</code>, 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:</p>
-
-<pre class="brush: js">if (posX % 13 === 0) {
- if (sprite === 5) {
- sprite = 0;
- } else {
- sprite++;
- }
-}</pre>
-
-<p>So the code only cycles the sprite once every 13 animation frames.</p>
-
-<p>...Actually, it's about every 6.5 frames, as we update <code>posX</code> (character's position on the screen) by two each frame:</p>
-
-<pre class="brush: js">if (posX &gt; width/2) {
- newStartPos = -( (width/2) + 102 );
- posX = Math.ceil(newStartPos / 13) * 13;
- console.log(posX);
-} else {
- posX += 2;
-}</pre>
-
-<p>This is the code that calculates how to update the position in each animation frame.</p>
-
-<p>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 <code>rotateCount</code> by one on each frame, instead of two.</p>
-
-<h2 id="Active_learning_a_reaction_game">Active learning: a reaction game</h2>
-
-<p>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 <kbd>A</kbd> key, and the other with the <kbd>L</kbd> key.</p>
-
-<p>When the <em>Start</em> 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 <code>"PLAYERS GO!!"</code> — once this happens, the first player to press their control button will win the game.</p>
-
-<p>{{EmbedGHLiveSample("learning-area/javascript/asynchronous/loops-and-intervals/reaction-game.html", '100%', 500)}}</p>
-
-<p>Let's work through this:</p>
-
-<ol>
- <li>
- <p>First of all, download the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/reaction-game-starter.html">starter file for the app</a>. 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.</p>
- </li>
- <li>
- <p>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:</p>
-
- <pre class="brush: js">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');</pre>
-
- <p>In order, these are:</p>
-
- <ol>
- <li>A reference to the spinner, so you can animate it.</li>
- <li>A reference to the {{htmlelement("div")}} element that contains the spinner, used for showing and hiding it.</li>
- <li>A rotate count. This determines how much you want to show the spinner rotated on each frame of the animation.</li>
- <li>A null start time. This will be populated with a start time when the spinner starts spinning.</li>
- <li>An uninitialized variable to later store the {{domxref("Window.requestAnimationFrame", "requestAnimationFrame()")}} call that animates the spinner.</li>
- <li>A reference to the Start button.</li>
- <li>A reference to the results paragraph.</li>
- </ol>
- </li>
- <li>
- <p>Next, below the previous lines of code, add the following function. It takes two numbers and returns a random number between the two. You'll need this to generate a random timeout interval later on.</p>
-
- <pre class="brush: js">function random(min,max) {
- var num = Math.floor(Math.random()*(max-min)) + min;
- return num;
-}</pre>
- </li>
- <li>
- <p>Next add  the <code>draw()</code> function, which animates the spinner. This is very similar to the version from the simple spinner example, earlier:</p>
-
- <pre class="brush: js">function draw(timestamp) {
- if(!startTime) {
- startTime = timestamp;
- }
-
- rotateCount = (timestamp - startTime) / 3;
-
- rotateCount %= 360;
-
- spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
- rAF = requestAnimationFrame(draw);
-}</pre>
- </li>
- <li>
- <p>Now it is time to set up the initial state of the app when the page first loads. Add the following two lines, which hide the results paragraph and spinner container using <code>display: none;</code>.</p>
-
- <pre class="brush: js">result.style.display = 'none';
-spinnerContainer.style.display = 'none';</pre>
- </li>
- <li>
- <p>Next, define a <code>reset()</code> 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:</p>
-
- <pre class="brush: js">function reset() {
- btn.style.display = 'block';
- result.textContent = '';
- result.style.display = 'none';
-}</pre>
- </li>
- <li>
- <p>Okay, enough preparation!  It's time to make the game playable! Add the following block to your code. The <code>start()</code> function calls <code>draw()</code> to start the spinner spinning and display it in the UI, hides the <em>Start</em> button so you can't mess up the game by starting it multiple times concurrently, and runs a <code>setTimeout()</code> call that runs a <code>setEndgame()</code> 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 <code>start()</code> function when it is clicked.</p>
-
- <pre class="brush: js">btn.addEventListener('click', start);
-
-function start() {
- draw();
- spinnerContainer.style.display = 'block';
- btn.style.display = 'none';
- setTimeout(setEndgame, random(5000,10000));
-}</pre>
-
- <div class="notecard note">
- <p><strong>Note</strong>: You'll see this example is calling <code>setTimeout()</code> without storing the return value. (So, not <code>let myTimeout = setTimeout(functionName, interval)</code>.) </p>
-
- <p>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!</p>
- </div>
-
- <p>The net result of the previous code is that when the <em>Start</em> 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 <code>setEndgame()</code> function, which you'll define next.</p>
- </li>
- <li>
- <p>Add the following function to your code next:</p>
-
- <pre class="brush: js">function setEndgame() {
- cancelAnimationFrame(rAF);
- spinnerContainer.style.display = 'none';
- result.style.display = 'block';
- result.textContent = 'PLAYERS GO!!';
-
- document.addEventListener('keydown', keyHandler);
-
- function keyHandler(e) {
- let isOver = false;
- console.log(e.key);
-
- if (e.key === "a") {
- result.textContent = 'Player 1 won!!';
- isOver = true;
- } else if (e.key === "l") {
- result.textContent = 'Player 2 won!!';
- isOver = true;
- }
-
- if (isOver) {
- document.removeEventListener('keydown', keyHandler);
- setTimeout(reset, 5000);
- }
- };
-}</pre>
-
- <p>Stepping through this:</p>
-
- <ol>
- <li>First, cancel the spinner animation with {{domxref("window.cancelAnimationFrame", "cancelAnimationFrame()")}} (it is always good to clean up unneeded processes), and hide the spinner container.</li>
- <li>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.</li>
- <li>Attach a <code><a href="/en-US/docs/Web/API/Document/keydown_event">keydown</a></code> event listener to the document. When any button is pressed down, the <code>keyHandler()</code> function is run.</li>
- <li>Inside <code>keyHandler()</code>, the code includes the event object as a parameter (represented by <code>e</code>) — 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.</li>
- <li>Set the variable <code>isOver</code> to false, so we can track whether the correct keys were pressed for player 1 or 2 to win. We don't want the game ending when a wrong key was pressed.</li>
- <li>Log <code>e.key</code> to the console, which is a useful way of finding out the <code>key</code> value of different keys you are pressing.</li>
- <li>When <code>e.key</code> is "a", display a message to say that Player 1 won, and when <code>e.key</code> is "l", display a message to say Player 2 won. (<strong>Note:</strong> This will only work with lowercase a and l — if an uppercase A or L is submitted (the key plus <kbd>Shift</kbd>), it is counted as a different key!) If one of these keys was pressed, set <code>isOver</code> to <code>true</code>.</li>
- <li>Only if <code>isOver</code> is <code>true</code>, remove the <code>keydown</code> 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 <code>setTimeout()</code> to call <code>reset()</code> after 5 seconds — as explained earlier, this function resets the game back to its original state so that a new game can be started.</li>
- </ol>
- </li>
-</ol>
-
-<p>That's it—you're all done!</p>
-
-<div class="notecard note">
-<p><strong>Note</strong>: If you get stuck, check out <a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/reaction-game.html">our version of the reaction game</a> (see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/reaction-game.html">source code</a> also).</p>
-</div>
-
-<h2 id="Conclusion">Conclusion</h2>
-
-<p>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.</p>
-
-<p>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}</p>
-
-<h2 id="In_this_module">In this module</h2>
-
-<ul>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">General asynchronous programming concepts</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">Introducing asynchronous JavaScript</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">Graceful asynchronous programming with Promises</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">Making asynchronous programming easier with async and await</a></li>
- <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">Choosing the right approach</a></li>
-</ul>
-
-<div id="gtx-trans" style="position: absolute; left: 70px; top: 13746px;">
-<div class="gtx-trans-icon"></div>
-</div>