Looping code - Learn web development

Looping code
============

* Previous
* Overview: Building blocks
* Next

Programming languages are very useful for rapidly completing repetitive tasks, from multiple basic calculations to just about any other situation where you've got a lot of similar items of work to complete. Here we'll look at the loop structures available in JavaScript that handle such needs.

|  |  |
| --- | --- |
| Prerequisites: |
 A basic understanding of HTML, CSS, and
 JavaScript first steps.
  |
| Objective: | To understand how to use loops in JavaScript. |

Why are loops useful?
---------------------

Loops are all about doing the same thing over and over again. Often, the code will be slightly different each time round the loop, or the same code will run but with different variables.

### Looping code example

Suppose we wanted to draw 100 random circles on a `<canvas>` element (press the *Update* button to run the example again and again to see different random sets):
```

<button>Update</button> <canvas></canvas>
```

```
html {
  width: 100%;
  height: inherit;
  background: #ddd;
}

canvas {
  display: block;
}

body {
  margin: 0;
}

button {
  position: absolute;
  top: 5px;
  left: 5px;
}
```

Here's the JavaScript code that implements this example:

```js
const btn = document.querySelector("button");
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");

document.addEventListener("DOMContentLoaded", () => {
  canvas.width = document.documentElement.clientWidth;
  canvas.height = document.documentElement.clientHeight;
});

function random(number) {
  return Math.floor(Math.random() \* number);
}

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (let i = 0; i < 100; i++) {
    ctx.beginPath();
    ctx.fillStyle = "rgb(255 0 0 / 50%)";
    ctx.arc(
      random(canvas.width),
      random(canvas.height),
      random(50),
      0,
      2 \* Math.PI,
    );
    ctx.fill();
  }
}

btn.addEventListener("click", draw);
```

### With and without a loop

You don't have to understand all the code for now, but let's look at the part of the code that actually draws the 100 circles:

```js
for (let i = 0; i < 100; i++) {
  ctx.beginPath();
  ctx.fillStyle = "rgb(255 0 0 / 50%)";
  ctx.arc(
    random(canvas.width),
    random(canvas.height),
    random(50),
    0,
    2 \* Math.PI,
  );
  ctx.fill();
}
```

* `random(x)`, defined earlier in the code, returns a whole number between `0` and `x-1`.

 You should get the basic idea — we are using a loop to run 100 iterations of this code, each one of which draws a circle in a random position on the page.
 The amount of code needed would be the same whether we were drawing 100 circles, 1000, or 10,000.
 Only one number has to change.

If we weren't using a loop here, we'd have to repeat the following code for every circle we wanted to draw:

```js
ctx.beginPath();
ctx.fillStyle = "rgb(255 0 0 / 50%)";
ctx.arc(
  random(canvas.width),
  random(canvas.height),
  random(50),
  0,
  2 \* Math.PI,
);
ctx.fill();
```

This would get very boring and difficult to maintain.

Looping through a collection
----------------------------

Most of the time when you use a loop, you will have a collection of items and want to do something with every item.

 One type of collection is the `Array`, which we met in the Arrays chapter of this course.
 But there are other collections in JavaScript as well, including `Set` and `Map`.

### The for...of loop

The basic tool for looping through a collection is the `for...of` loop:

```js
const cats = ["Leopard", "Serval", "Jaguar", "Tiger", "Caracal", "Lion"];

for (const cat of cats) {
  console.log(cat);
}
```

In this example, `for (const cat of cats)` says:

1. Given the collection `cats`, get the first item in the collection.
2. Assign it to the variable `cat` and then run the code between the curly braces `{}`.
3. Get the next item, and repeat (2) until you've reached the end of the collection.
### map() and filter()

JavaScript also has more specialized loops for collections, and we'll mention two of them here.

You can use `map()` to do something to each item in a collection and create a new collection containing the changed items:

```js
function toUpper(string) {
  return string.toUpperCase();
}

const cats = ["Leopard", "Serval", "Jaguar", "Tiger", "Caracal", "Lion"];

const upperCats = cats.map(toUpper);

console.log(upperCats);
// [ "LEOPARD", "SERVAL", "JAGUAR", "TIGER", "CARACAL", "LION" ]
```

Here we pass a function into `cats.map()`, and `map()` calls the function once for each item in the array, passing in the item. It then adds the return value from each function call to a new array, and finally returns the new array. In this case the function we provide converts the item to uppercase, so the resulting array contains all our cats in uppercase:

```js
[ "LEOPARD", "SERVAL", "JAGUAR", "TIGER", "CARACAL", "LION" ]
```

You can use `filter()` to test each item in a collection, and create a new collection containing only items that match:

```js
function lCat(cat) {
  return cat.startsWith("L");
}

const cats = ["Leopard", "Serval", "Jaguar", "Tiger", "Caracal", "Lion"];

const filtered = cats.filter(lCat);

console.log(filtered);
// [ "Leopard", "Lion" ]
```

 This looks a lot like `map()`, except the function we pass in returns a boolean: if it returns `true`, then the item is included in the new array.
 Our function tests that the item starts with the letter "L", so the result is an array containing only cats whose names start with "L":

```js
[ "Leopard", "Lion" ]
```

 Note that `map()` and `filter()` are both often used with *function expressions*, which we will learn about in the Functions module.
 Using function expressions we could rewrite the example above to be much more compact:

```js
const cats = ["Leopard", "Serval", "Jaguar", "Tiger", "Caracal", "Lion"];

const filtered = cats.filter((cat) => cat.startsWith("L"));
console.log(filtered);
// [ "Leopard", "Lion" ]
```

The standard for loop
---------------------

 In the "drawing circles" example above, you don't have a collection of items to loop through: you really just want to run the same code 100 times.
 In a case like that, you should use the `for` loop.
 This has the following syntax:

```js
for (initializer; condition; final-expression) {
  // code to run
}
```

Here we have:

1. The keyword `for`, followed by some parentheses.
2. Inside the parentheses we have three items, separated by semicolons:
    1. An **initializer** — this is usually a variable set to a number, which is incremented to count the number of times the loop has run.
     It is also sometimes referred to as a **counter variable**.
    2. A **condition** — this defines when the loop should stop looping.
     This is generally an expression featuring a comparison operator, a test to see if the exit condition has been met.
    3. A **final-expression** — this is always evaluated (or run) each time the loop has gone through a full iteration.
     It usually serves to increment (or in some cases decrement) the counter variable, to bring it closer to the point where the condition is no longer `true`.
3. Some curly braces that contain a block of code — this code will be run each time the loop iterates.
### Calculating squares

Let's look at a real example so we can visualize what these do more clearly.
```

<button id="calculate">Calculate</button>
<button id="clear">Clear</button>
<pre id="results"></pre>
```

```js
const results = document.querySelector("#results");

function calculate() {
  for (let i = 1; i < 10; i++) {
    const newResult = `${i} x ${i} = ${i \* i}`;
    results.textContent += `${newResult}\n`;
  }
  results.textContent += "\nFinished!";
}

const calculateBtn = document.querySelector("#calculate");
const clearBtn = document.querySelector("#clear");

calculateBtn.addEventListener("click", calculate);
clearBtn.addEventListener("click", () => (results.textContent = ""));
```

This gives us the following output:

This code calculates squares for the numbers from 1 to 9, and writes out the result. The core of the code is the `for` loop that performs the calculation.

Let's break down the `for (let i = 1; i < 10; i++)` line into its three pieces:

1. `let i = 1`: the counter variable, `i`, starts at `1`. Note that we have to use `let` for the counter, because we're reassigning it each time we go round the loop.
2. `i < 10`: keep going round the loop for as long as `i` is smaller than `10`.
3. `i++`: add one to `i` each time round the loop.

Inside the loop, we calculate the square of the current value of `i`, that is: `i * i`. We create a string expressing the calculation we made and the result, and add this string to the output text. We also add `\n`, so the next string we add will begin on a new line. So:

1. During the first run, `i = 1`, so we will add `1 x 1 = 1`.
2. During the second run, `i = 2`, so we will add `2 x 2 = 4`.
3. And so on…
4. When `i` becomes equal to `10` we will stop running the loop and move straight to the next bit of code below the loop, printing out the `Finished!` message on a new line.
### Looping through collections with a for loop

You can use a `for` loop to iterate through a collection, instead of a `for...of` loop.

Let's look again at our `for...of` example above:

```js
const cats = ["Leopard", "Serval", "Jaguar", "Tiger", "Caracal", "Lion"];

for (const cat of cats) {
  console.log(cat);
}
```

We could rewrite that code like this:

```js
const cats = ["Leopard", "Serval", "Jaguar", "Tiger", "Caracal", "Lion"];

for (let i = 0; i < cats.length; i++) {
  console.log(cats[i]);
}
```

 In this loop we're starting `i` at `0`, and stopping when `i` reaches the length of the array.
 Then inside the loop, we're using `i` to access each item in the array in turn.

 This works just fine, and in early versions of JavaScript, `for...of` didn't exist, so this was the standard way to iterate through an array.
 However, it offers more chances to introduce bugs into your code. For example:

* you might start `i` at `1`, forgetting that the first array index is zero, not 1.
* you might stop at `i <= cats.length`, forgetting that the last array index is at `length - 1`.

For reasons like this, it's usually best to use `for...of` if you can.

 Sometimes you still need to use a `for` loop to iterate through an array.
 For example, in the code below we want to log a message listing our cats:

```js
const cats = ["Pete", "Biggles", "Jasmine"];

let myFavoriteCats = "My cats are called ";

for (const cat of cats) {
  myFavoriteCats += `${cat}, `;
}

console.log(myFavoriteCats); // "My cats are called Pete, Biggles, Jasmine, "
```

The final output sentence isn't very well-formed:
```

My cats are called Pete, Biggles, Jasmine,
```

We'd prefer it to handle the last cat differently, like this:
```

My cats are called Pete, Biggles, and Jasmine.
```

But to do this we need to know when we are on the final loop iteration, and to do that we can use a `for` loop and examine the value of `i`:

```js
const cats = ["Pete", "Biggles", "Jasmine"];

let myFavoriteCats = "My cats are called ";

for (let i = 0; i < cats.length; i++) {
  if (i === cats.length - 1) {
    // We are at the end of the array
    myFavoriteCats += `and ${cats[i]}.`;
  } else {
    myFavoriteCats += `${cats[i]}, `;
  }
}

console.log(myFavoriteCats); // "My cats are called Pete, Biggles, and Jasmine."
```

Exiting loops with break
------------------------

 If you want to exit a loop before all the iterations have been completed, you can use the break statement.
 We already met this in the previous article when we looked at switch statements — when a case is met in a switch statement that matches the input expression, the `break` statement immediately exits the switch statement and moves on to the code after it.

It's the same with loops — a `break` statement will immediately exit the loop and make the browser move on to any code that follows it.

 Say we wanted to search through an array of contacts and telephone numbers and return just the number we wanted to find?
 First, some simple HTML — a text `<input>` allowing us to enter a name to search for, a `<button>` element to submit a search, and a `<p>` element to display the results in:

```html
<label for="search">Search by contact name: </label>
<input id="search" type="text" />
<button>Search</button>

<p></p>
```

Now on to the JavaScript:

```js
const contacts = [
  "Chris:2232322",
  "Sarah:3453456",
  "Bill:7654322",
  "Mary:9998769",
  "Dianne:9384975",
];
const para = document.querySelector("p");
const input = document.querySelector("input");
const btn = document.querySelector("button");

btn.addEventListener("click", () => {
  const searchName = input.value.toLowerCase();
  input.value = "";
  input.focus();
  para.textContent = "";
  for (const contact of contacts) {
    const splitContact = contact.split(":");
    if (splitContact[0].toLowerCase() === searchName) {
      para.textContent = `${splitContact[0]}'s number is ${splitContact[1]}.`;
      break;
    }
  }
  if (para.textContent === "") {
    para.textContent = "Contact not found.";
  }
});
```

1. First of all, we have some variable definitions — we have an array of contact information, with each item being a string containing a name and phone number separated by a colon.
2. Next, we attach an event listener to the button (`btn`) so that when it is pressed some code is run to perform the search and return the results.
3. We store the value entered into the text input in a variable called `searchName`, before then emptying the text input and focusing it again, ready for the next search.
 Note that we also run the `toLowerCase()` method on the string, so that searches will be case-insensitive.
4. Now on to the interesting part, the `for...of` loop:
    1. Inside the loop, we first split the current contact at the colon character, and store the resulting two values in an array called `splitContact`.
    2. We then use a conditional statement to test whether `splitContact[0]` (the contact's name, again lower-cased with `toLowerCase()`) is equal to the inputted `searchName`.
     If it is, we enter a string into the paragraph to report what the contact's number is, and use `break` to end the loop.
5. After the loop, we check whether we set a contact, and if not we set the paragraph text to "Contact not found.".

**Note:** You can view the full source code on GitHub too (also see it running live).

Skipping iterations with continue
---------------------------------

 The continue statement works similarly to `break`, but instead of breaking out of the loop entirely, it skips to the next iteration of the loop.
 Let's look at another example that takes a number as an input, and returns only the numbers that are squares of integers (whole numbers).

The HTML is basically the same as the last example — a simple numeric input, and a paragraph for output.

```html
<label for="number">Enter number: </label>
<input id="number" type="number" />
<button>Generate integer squares</button>

<p>Output:</p>
```

The JavaScript is mostly the same too, although the loop itself is a bit different:

```js
const para = document.querySelector("p");
const input = document.querySelector("input");
const btn = document.querySelector("button");

btn.addEventListener("click", () => {
  para.textContent = "Output: ";
  const num = input.value;
  input.value = "";
  input.focus();
  for (let i = 1; i <= num; i++) {
    let sqRoot = Math.sqrt(i);
    if (Math.floor(sqRoot) !== sqRoot) {
      continue;
    }
    para.textContent += `${i} `;
  }
});
```

Here's the output:

1. In this case, the input should be a number (`num`). The `for` loop is given a counter starting at 1 (as we are not interested in 0 in this case), an exit condition that says the loop will stop when the counter becomes bigger than the input `num`, and an iterator that adds 1 to the counter each time.
2. Inside the loop, we find the square root of each number using Math.sqrt(i), then check whether the square root is an integer by testing whether it is the same as itself when it has been rounded down to the nearest integer (this is what Math.floor() does to the number it is passed).
3. If the square root and the rounded down square root do not equal one another (`!==`), it means that the square root is not an integer, so we are not interested in it. In such a case, we use the `continue` statement to skip on to the next loop iteration without recording the number anywhere.
4. If the square root is an integer, we skip past the `if` block entirely, so the `continue` statement is not executed; instead, we concatenate the current `i` value plus a space at the end of the paragraph content.

**Note:** You can view the full source code on GitHub too (also see it running live).

while and do...while
--------------------

`for` is not the only type of loop available in JavaScript. There are actually many others and, while you don't need to understand all of these now, it is worth having a look at the structure of a couple of others so that you can recognize the same features at work in a slightly different way.

First, let's have a look at the while loop. This loop's syntax looks like so:

```js
initializer
while (condition) {
  // code to run

  final-expression
}
```

 This works in a very similar way to the `for` loop, except that the initializer variable is set before the loop, and the final-expression is included inside the loop after the code to run, rather than these two items being included inside the parentheses.
 The condition is included inside the parentheses, which are preceded by the `while` keyword rather than `for`.

 The same three items are still present, and they are still defined in the same order as they are in the for loop.
 This is because you must have an initializer defined before you can check whether or not the condition is true.
 The final-expression is then run after the code inside the loop has run (an iteration has been completed), which will only happen if the condition is still true.

Let's have a look again at our cats list example, but rewritten to use a while loop:

```js
const cats = ["Pete", "Biggles", "Jasmine"];

let myFavoriteCats = "My cats are called ";

let i = 0;

while (i < cats.length) {
  if (i === cats.length - 1) {
    myFavoriteCats += `and ${cats[i]}.`;
  } else {
    myFavoriteCats += `${cats[i]}, `;
  }

  i++;
}

console.log(myFavoriteCats); // "My cats are called Pete, Biggles, and Jasmine."
```

**Note:** This still works just the same as expected — have a look at it running live on GitHub (also view the full source code).

The do...while loop is very similar, but provides a variation on the while structure:

```js
initializer
do {
  // code to run

  final-expression
} while (condition)
```

In this case, the initializer again comes first, before the loop starts. The keyword directly precedes the curly braces containing the code to run and the final expression.

The main difference between a `do...while` loop and a `while` loop is that *the code inside a `do...while` loop is always executed at least once*. That's because the condition comes after the code inside the loop. So we always run that code, then check to see if we need to run it again. In `while` and `for` loops, the check comes first, so the code might never be executed.

Let's rewrite our cat listing example again to use a `do...while` loop:

```js
const cats = ["Pete", "Biggles", "Jasmine"];

let myFavoriteCats = "My cats are called ";

let i = 0;

do {
  if (i === cats.length - 1) {
    myFavoriteCats += `and ${cats[i]}.`;
  } else {
    myFavoriteCats += `${cats[i]}, `;
  }

  i++;
} while (i < cats.length);

console.log(myFavoriteCats); // "My cats are called Pete, Biggles, and Jasmine."
```

**Note:** Again, this works just the same as expected — have a look at it running live on GitHub (also view the full source code).

**Warning:** With while and do...while — as with all loops — you must make sure that the initializer is incremented or, depending on the case, decremented, so the condition eventually becomes false.
 If not, the loop will go on forever, and either the browser will force it to stop, or it will crash. This is called an **infinite loop**.

Active learning: Launch countdown
---------------------------------

 In this exercise, we want you to print out a simple launch countdown to the output box, from 10 down to Blastoff.
 Specifically, we want you to:

* Loop from 10 down to 0. We've provided you with an initializer — `let i = 10;`.
* For each iteration, create a new paragraph and append it to the output `<div>`, which we've selected using `const output = document.querySelector('.output');`.
 In comments, we've provided you with three code lines that need to be used somewhere inside the loop:
    + `const para = document.createElement('p');` — creates a new paragraph.
    + `output.appendChild(para);` — appends the paragraph to the output `<div>`.
    + `para.textContent =` — makes the text inside the paragraph equal to whatever you put on the right-hand side, after the equals sign.
* Different iteration numbers require different text to be put in the paragraph for that iteration (you'll need a conditional statement and multiple `para.textContent =` lines):
    + If the number is 10, print "Countdown 10" to the paragraph.
    + If the number is 0, print "Blast off!" to the paragraph.
    + For any other number, print just the number to the paragraph.
* Remember to include an iterator! However, in this example we are counting down after each iteration, not up, so you **don't** want `i++` — how do you iterate downwards?

**Note:** If you start typing the loop (for example (while(i>=0)), the browser might get stuck because you have not yet entered the end condition. So be careful with this. You can start writing your code in a comment to deal with this issue and remove the comment after you finish.

 If you make a mistake, you can always reset the example with the "Reset" button.
 If you get really stuck, press "Show solution" to see a solution.
```

<h2>Live output</h2>
<div class="output" style="height: 410px;overflow: auto;"></div>

<h2>Editable code</h2>
<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>
<textarea id="code" class="playable-code" style="height: 300px;width: 95%">
let output = document.querySelector('.output');
output.innerHTML = '';

// let i = 10;

// const para = document.createElement('p');
// para.textContent = ;
// output.appendChild(para);
</textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```css
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
let code = textarea.value;
let userEntry = textarea.value;

function updateCode() {
  eval(textarea.value);
}

reset.addEventListener("click", function () {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = jsSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", function () {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

let jsSolution = `const output = document.querySelector('.output');
output.innerHTML = '';

let i = 10;

while (i >= 0) {
 const para = document.createElement('p');
 if (i === 10) {
 para.textContent = \`Countdown \${i}\`;
 } else if (i === 0) {
 para.textContent = 'Blast off!';
 } else {
 para.textContent = i;
 }

 output.appendChild(para);

 i--;
}`;

let solutionEntry = jsSolution;

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead

textarea.onkeydown = function (e) {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  const scrollPos = textarea.scrollTop;
  let caretPos = textarea.selectionStart;
  const front = textarea.value.substring(0, caretPos);
  const back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );

  textarea.value = front + text + back;
  caretPos += text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// Update the saved userCode every time the user updates the text area code

textarea.onkeyup = () => {
  // We only want to save the state when the user code is being shown,
  // not the solution, so that solution is not saved over the user code
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

Active learning: Filling in a guest list
----------------------------------------

In this exercise, we want you to take a list of names stored in an array and put them into a guest list. But it's not quite that easy — we don't want to let Phil and Lola in because they are greedy and rude, and always eat all the food! We have two lists, one for guests to admit, and one for guests to refuse.

Specifically, we want you to:

* Write a loop that will iterate through the `people` array.
* During each loop iteration, check if the current array item is equal to "Phil" or "Lola" using a conditional statement:
    + If it is, concatenate the array item to the end of the `refused` paragraph's `textContent`, followed by a comma and a space.
    + If it isn't, concatenate the array item to the end of the `admitted` paragraph's `textContent`, followed by a comma and a space.

We've already provided you with:

* `refused.textContent +=` — the beginnings of a line that will concatenate something at the end of `refused.textContent`.
* `admitted.textContent +=` — the beginnings of a line that will concatenate something at the end of `admitted.textContent`.

 Extra bonus question — after completing the above tasks successfully, you will be left with two lists of names, separated by commas, but they will be untidy — there will be a comma at the end of each one.
 Can you work out how to write lines that slice the last comma off in each case, and add a full stop to the end?
 Have a look at the Useful string methods article for help.

 If you make a mistake, you can always reset the example with the "Reset" button.
 If you get really stuck, press "Show solution" to see a solution.
```

<h2>Live output</h2>
<div class="output" style="height: 100px;overflow: auto;">
  <p class="admitted">Admit:</p>
  <p class="refused">Refuse:</p>
</div>

<h2>Editable code</h2>
<p class="a11y-label">
  Press Esc to move focus away from the code area (Tab inserts a tab character).
</p>
<textarea id="code" class="playable-code" style="height: 400px;width: 95%">
const people = ['Chris', 'Anne', 'Colin', 'Terri', 'Phil', 'Lola', 'Sam', 'Kay', 'Bruce'];

const admitted = document.querySelector('.admitted');
const refused = document.querySelector('.refused');
admitted.textContent = 'Admit: ';
refused.textContent = 'Refuse: ';

// loop starts here

// refused.textContent += ;
// admitted.textContent += ;

</textarea>

<div class="playable-buttons">
  <input id="reset" type="button" value="Reset" />
  <input id="solution" type="button" value="Show solution" />
</div>
```

```
html {
  font-family: sans-serif;
}

h2 {
  font-size: 16px;
}

.a11y-label {
  margin: 0;
  text-align: right;
  font-size: 0.7rem;
  width: 98%;
}

body {
  margin: 10px;
  background: #f5f9fa;
}
```

```
const textarea = document.getElementById("code");
const reset = document.getElementById("reset");
const solution = document.getElementById("solution");
let code = textarea.value;
let userEntry = textarea.value;

function updateCode() {
  eval(textarea.value);
}

reset.addEventListener("click", function () {
  textarea.value = code;
  userEntry = textarea.value;
  solutionEntry = jsSolution;
  solution.value = "Show solution";
  updateCode();
});

solution.addEventListener("click", function () {
  if (solution.value === "Show solution") {
    textarea.value = solutionEntry;
    solution.value = "Hide solution";
  } else {
    textarea.value = userEntry;
    solution.value = "Show solution";
  }
  updateCode();
});

const jsSolution = `
const people = ['Chris', 'Anne', 'Colin', 'Terri', 'Phil', 'Lola', 'Sam', 'Kay', 'Bruce'];

const admitted = document.querySelector('.admitted');
const refused = document.querySelector('.refused');

admitted.textContent = 'Admit: ';
refused.textContent = 'Refuse: ';

for (const person of people) {
 if (person === 'Phil' || person === 'Lola') {
 refused.textContent += \`\${person}, \`;
 } else {
 admitted.textContent += \`\${person}, \`;
 }
}

refused.textContent = refused.textContent.slice(0,refused.textContent.length-2) + '.';
admitted.textContent = admitted.textContent.slice(0,admitted.textContent.length-2) + '.';`;

let solutionEntry = jsSolution;

textarea.addEventListener("input", updateCode);
window.addEventListener("load", updateCode);

// stop tab key tabbing out of textarea and
// make it write a tab at the caret position instead

textarea.onkeydown = function (e) {
  if (e.keyCode === 9) {
    e.preventDefault();
    insertAtCaret("\t");
  }

  if (e.keyCode === 27) {
    textarea.blur();
  }
};

function insertAtCaret(text) {
  const scrollPos = textarea.scrollTop;
  let caretPos = textarea.selectionStart;
  const front = textarea.value.substring(0, caretPos);
  const back = textarea.value.substring(
    textarea.selectionEnd,
    textarea.value.length,
  );

  textarea.value = front + text + back;
  caretPos += text.length;
  textarea.selectionStart = caretPos;
  textarea.selectionEnd = caretPos;
  textarea.focus();
  textarea.scrollTop = scrollPos;
}

// Update the saved userCode every time the user updates the text area code

textarea.onkeyup = () => {
  // We only want to save the state when the user code is being shown,
  // not the solution, so that solution is not saved over the user code
  if (solution.value === "Show solution") {
    userEntry = textarea.value;
  } else {
    solutionEntry = textarea.value;
  }

  updateCode();
};
```

Which loop type should you use?
-------------------------------

If you're iterating through an array or some other object that supports it, and don't need access to the index position of each item, then `for...of` is the best choice. It's easier to read and there's less to go wrong.

 For other uses, `for`, `while`, and `do...while` loops are largely interchangeable.
 They can all be used to solve the same problems, and which one you use will largely depend on your personal preference — which one you find easiest to remember or most intuitive.
 We would recommend `for`, at least to begin with, as it is probably the easiest for remembering everything — the initializer, condition, and final-expression all have to go neatly into the parentheses, so it is easy to see where they are and check that you aren't missing them.

Let's have a look at them all again.

First `for...of`:

```js
for (const item of array) {
  // code to run
}
```

`for`:

```js
for (initializer; condition; final-expression) {
  // code to run
}
```

`while`:

```js
initializer
while (condition) {
  // code to run

  final-expression
}
```

and finally `do...while`:

```js
initializer
do {
  // code to run

  final-expression
} while (condition)
```

**Note:** There are other loop types/features too, which are useful in advanced/specialized situations and beyond the scope of this article. If you want to go further with your loop learning, read our advanced Loops and iteration guide.

Test your skills!
-----------------

You've reached the end of this article, but can you remember the most important information? You can find some further tests to verify that you've retained this information before you move on — see Test your skills: Loops.

Conclusion
----------

 This article has revealed to you the basic concepts behind, and different options available when looping code in JavaScript.
 You should now be clear on why loops are a good mechanism for dealing with repetitive code and raring to use them in your own examples!

If there is anything you didn't understand, feel free to read through the article again, or contact us to ask for help.

See also
--------

* Loops and iteration in detail
* for...of reference
* for statement reference
* while and do...while references
* break and continue references
* Previous
* Overview: Building blocks
* Next


Build your own function - Learn web development

Build your own function
=======================

* Previous
* Overview: Building blocks
* Next

With most of the essential theory dealt with in the previous article, this article provides practical experience. Here you will get some practice building your own, custom function. Along the way, we'll also explain some useful details of dealing with functions.

|  |  |
| --- | --- |
| Prerequisites: |
 A basic understanding of HTML, CSS, and
 JavaScript first steps. Also, Functions — reusable blocks of code.
  |
| Objective: |
 To provide some practice in building a custom function, and explain a
 few more useful associated details.
  |

Active learning: Let's build a function
---------------------------------------

The custom function we are going to build will be called `displayMessage()`. It will display a custom message box on a web page and will act as a customized replacement for a browser's built-in alert() function. We've seen this before, but let's just refresh our memories. Type the following in your browser's JavaScript console, on any page you like:

```js
alert("This is a message");
```

The `alert` function takes a single argument — the string that is displayed in the alert box. Try varying the string to change the message.

The `alert` function is limited: you can alter the message, but you can't easily vary anything else, such as the color, icon, or anything else. We'll build one that will prove to be more fun.

**Note:** This example should work in all modern browsers fine, but the styling might look a bit funny in slightly older browsers. We'd recommend you do this exercise in a modern browser like Firefox, Opera, or Chrome.

The basic function
------------------

To begin with, let's put together a basic function.

**Note:** For function naming conventions, you should follow the same rules as variable naming conventions. This is fine, as you can tell them apart — function names appear with parentheses after them, and variables don't.

1. Start by accessing the function-start.html file and making a local copy. You'll see that the HTML is simple — the body contains just a single button. We've also provided some basic CSS to style the custom message box, and an empty `<script>` element to put our JavaScript in.
2. Next, add the following inside the `<script>` element:

```js
function displayMessage() {
  ...
}
```

We start off with the keyword `function`, which means we are defining a function. This is followed by the name we want to give to our function, a set of parentheses, and a set of curly braces. Any parameters we want to give to our function go inside the parentheses, and the code that runs when we call the function goes inside the curly braces.
3. Finally, add the following code inside the curly braces:

```js
const body = document.body;

const panel = document.createElement("div");
panel.setAttribute("class", "msgBox");
body.appendChild(panel);

const msg = document.createElement("p");
msg.textContent = "This is a message box";
panel.appendChild(msg);

const closeBtn = document.createElement("button");
closeBtn.textContent = "x";
panel.appendChild(closeBtn);

closeBtn.addEventListener("click", () =>
  panel.parentNode.removeChild(panel),
);
```

This is quite a lot of code to go through, so we'll walk you through it bit by bit.

The first line selects the `<body>` element by using the DOM API to get the `body` property of the global `document` object, and assigning that to a constant called `body`, so we can do things to it later on:

```js
const body = document.body;
```

The next section uses a DOM API function called `document.createElement()` to create a `<div>` element and store a reference to it in a constant called `panel`. This element will be the outer container of our message box.

We then use yet another DOM API function called `Element.setAttribute()` to set a `class` attribute on our panel with a value of `msgBox`. This is to make it easier to style the element — if you look at the CSS on the page, you'll see that we are using a `.msgBox` class selector to style the message box and its contents.

Finally, we call a DOM function called `Node.appendChild()` on the `body` constant we stored earlier, which nests one element inside the other as a child of it. We specify the panel `<div>` as the child we want to append inside the `<body>` element. We need to do this as the element we created won't just appear on the page on its own — we need to specify where to put it.

```js
const panel = document.createElement("div");
panel.setAttribute("class", "msgBox");
body.appendChild(panel);
```

The next two sections make use of the same `createElement()` and `appendChild()` functions we've already seen to create two new elements — a `<p>` and a `<button>` — and insert them in the page as children of the panel `<div>`. We use their `Node.textContent` property — which represents the text content of an element — to insert a message inside the paragraph, and an "x" inside the button. This button will be what needs to be clicked/activated when the user wants to close the message box.

```js
const msg = document.createElement("p");
msg.textContent = "This is a message box";
panel.appendChild(msg);

const closeBtn = document.createElement("button");
closeBtn.textContent = "x";
panel.appendChild(closeBtn);
```

Finally, we call `addEventListener()` to add a function that will be called when the user clicks the "close" button. The code will delete the whole panel from the page — to close the message box.

Briefly, the `addEventListener()` method is provided by the button (or in fact, any element on the page) that can be passed a function and the name of an event. In this case, the name of the event is 'click', meaning that when the user clicks the button, the function will run. You'll learn a lot more about events in our events article. The line inside the function uses the `Node.removeChild()` DOM API function to specify that we want to remove a specific child element of the HTML element — in this case, the panel `<div>`.

```js
closeBtn.addEventListener("click", () => panel.parentNode.removeChild(panel));
```

Basically, this whole block of code is generating a block of HTML that looks like so, and inserting it into the page:

```html
<div class="msgBox">
  <p>This is a message box</p>
  <button>x</button>
</div>
```

That was a lot of code to work through — don't worry too much if you don't remember exactly how every bit of it works right now! The main part we want to focus on here is the function's structure and usage, but we wanted to show something interesting for this example.

Calling the function
--------------------

You've now got your function definition written into your `<script>` element just fine, but it will do nothing as it stands.

1. Try including the following line below your function to call it:

```js
displayMessage();
```

This line invokes the function, making it run immediately. When you save your code and reload it in the browser, you'll see the little message box appear immediately, only once. We are only calling it once, after all.
2. Now open your browser developer tools on the example page, go to the JavaScript console and type the line again there, you'll see it appear again! So this is fun — we now have a reusable function that we can call any time we like.
 But we probably want it to appear in response to user and system actions. In a real application, such a message box would probably be called in response to new data being available, or an error having occurred, or the user trying to delete their profile ("are you sure about this?"), or the user adding a new contact and the operation completing successfully, etc.
 In this demo, we'll get the message box to appear when the user clicks the button.
3. Delete the previous line you added.
4. Next, we'll select the button and store a reference to it in a constant. Add the following line to your code, above the function definition:

```js
const btn = document.querySelector("button");
```

5. Finally, add the following line below the previous one:

```js
btn.addEventListener("click", displayMessage);
```

In a similar way to our closeBtn's click event handler, here we are calling some code in response to a button being clicked. But in this case, instead of calling an anonymous function containing some code, we are calling our `displayMessage()` function by name.
6. Try saving and refreshing the page — now you should see the message box appear when you click the button.

You might be wondering why we haven't included the parentheses after the function name. This is because we don't want to call the function immediately — only after the button has been clicked. If you try changing the line to

```js
btn.addEventListener("click", displayMessage());
```

and saving and reloading, you'll see that the message box appears without the button being clicked! The parentheses in this context are sometimes called the "function invocation operator". You only use them when you want to run the function immediately in the current scope. In the same respect, the code inside the anonymous function is not run immediately, as it is inside the function scope.

If you tried the last experiment, make sure to undo the last change before carrying on.

Improving the function with parameters
--------------------------------------

As it stands, the function is still not very useful — we don't want to just show the same default message every time. Let's improve our function by adding some parameters, allowing us to call it with some different options.

1. First of all, update the first line of the function:

```js
function displayMessage() {
```

to this:

```js
function displayMessage(msgText, msgType) {
```

Now when we call the function, we can provide two variable values inside the parentheses to specify the message to display in the message box, and the type of message it is.
2. To make use of the first parameter, update the following line inside your function:

```js
msg.textContent = "This is a message box";
```

to

```js
msg.textContent = msgText;
```

3. Last but not least, you now need to update your function call to include some updated message text. Change the following line:

```js
btn.addEventListener("click", displayMessage);
```

to this block:

```js
btn.addEventListener("click", () =>
  displayMessage("Woo, this is a different message!"),
);
```

If we want to specify parameters inside parentheses for the function we are calling, then we can't call it directly — we need to put it inside an anonymous function so that it isn't in the immediate scope and therefore isn't called immediately. Now it will not be called until the button is clicked.
4. Reload and try the code again and you'll see that it still works just fine, except that now you can also vary the message inside the parameter to get different messages displayed in the box!
### A more complex parameter

On to the next parameter. This one is going to involve slightly more work — we are going to set it so that depending on what the `msgType` parameter is set to, the function will display a different icon and a different background color.

1. First of all, download the icons needed for this exercise (warning and chat) from GitHub. Save them in a new folder called `icons` in the same location as your HTML file.

**Note:** The warning and chat icons were originally found on iconfinder.com, and designed by Nazarrudin Ansyari — Thanks! (The actual icon pages were since moved or removed.)
2. Next, find the CSS inside your HTML file. We'll make a few changes to make way for the icons. First, update the `.msgBox` width from:

```css
width: 200px;
```

to

```css
width: 242px;
```

3. Next, add the following lines inside the `.msgBox p { }` rule:

```css
padding-left: 82px;
background-position: 25px center;
background-repeat: no-repeat;
```

4. Now we need to add code to our `displayMessage()` function to handle displaying the icons. Add the following block just above the closing curly brace (`}`) of your function:

```js
if (msgType === "warning") {
  msg.style.backgroundImage = "url(icons/warning.png)";
  panel.style.backgroundColor = "red";
} else if (msgType === "chat") {
  msg.style.backgroundImage = "url(icons/chat.png)";
  panel.style.backgroundColor = "aqua";
} else {
  msg.style.paddingLeft = "20px";
}
```

Here, if the `msgType` parameter is set as `'warning'`, the warning icon is displayed and the panel's background color is set to red. If it is set to `'chat'`, the chat icon is displayed and the panel's background color is set to aqua blue. If the `msgType` parameter is not set at all (or to something different), then the `else { }` part of the code comes into play, and the paragraph is given default padding and no icon, with no background panel color set either. This provides a default state if no `msgType` parameter is provided, meaning that it is an optional parameter!
5. Let's test out our updated function, try updating the `displayMessage()` call from this:

```js
displayMessage("Woo, this is a different message!");
```

to one of these:

```js
displayMessage("Your inbox is almost full — delete some mails", "warning");
displayMessage("Brian: Hi there, how are you today?", "chat");
```

You can see how useful our (now not so) little function is becoming.

**Note:** If you have trouble getting the example to work, feel free to check your code against the finished version on GitHub (see it running live also), or ask us for help.

Test your skills!
-----------------

You've reached the end of this article, but can you remember the most important information? You can find some further tests to verify that you've retained this information before you move on — see Test your skills: Functions. These tests require skills that are covered in the next article, so you might want to read that first before trying the test.

Conclusion
----------

Congratulations on reaching the end! This article took you through the entire process of building up a practical custom function, which with a bit more work could be transplanted into a real project. In the next article, we'll wrap up functions by explaining another essential related concept — return values.

* Previous
* Overview: Building blocks
* Next


Test your skills: Conditionals - Learn web development

Test your skills: Conditionals
==============================

The aim of this skill test is to assess whether you've understood our Making decisions in your code — conditionals article.

**Note:** You can try solutions by downloading the code and putting it in an online editor such as CodePen, JSFiddle, or Glitch.
 If there is an error, it will be logged in the results panel on the page or into the browser's JavaScript console to help you.

If you get stuck, you can reach out to us in one of our communication channels.

Conditionals 1
--------------

In this task you are provided with two variables:

* `season` — contains a string that says what the current season is.
* `response` — begins uninitialized, but is later used to store a response that will be printed to the output panel.

We want you to create a conditional that checks whether `season` contains the string "summer", and if so assigns a string to `response` that gives the user an appropriate message about the season. If not, it should assign a generic string to `response` that tells the user we don't know what season it is.

To finish off, you should then add another test that checks whether `season` contains the string "winter", and again assigns an appropriate string to `response`.

Try updating the live code below to recreate the finished example:

Download the starting point for this task to work in your own editor or in an online editor.

Conditionals 2
--------------

For this task you are given three variables:

* `machineActive` — contains an indicator of whether the answer machine is switched on or not (`true`/`false`)
* `score` — Contains your score in an imaginary game. This score is fed into the answer machine, which provides a response to indicate how well you did.
* `response` — begins uninitialized, but is later used to store a response that will be printed to the output panel.

You need to create an `if...else` structure that checks whether the machine is switched on and puts a message into the `response` variable if it isn't, telling the user to switch the machine on.

Inside the first `if...else`, you need to nest another `if...else` that puts appropriate messages into the `response` variable depending on what the value of score is — if the machine is turned on. The different conditional tests (and resulting responses) are as follows:

* Score of less than 0 or more than 100 — "This is not possible, an error has occurred."
* Score of 0 to 19 — "That was a terrible score — total fail!"
* Score of 20 to 39 — "You know some things, but it\'s a pretty bad score. Needs improvement."
* Score of 40 to 69 — "You did a passable job, not bad!"
* Score of 70 to 89 — "That\'s a great score, you really know your stuff."
* Score of 90 to 100 — "What an amazing score! Did you cheat? Are you for real?"

Try updating the live code below to recreate the finished example. After you've entered your code, try changing `machineActive` to `true`, to see if it works.

Download the starting point for this task to work in your own editor or in an online editor.

Conditionals 3
--------------

For the final task you are given four variables:

* `machineActive` — contains an indicator of whether the login machine is switched on or not (`true`/`false`).
* `pwd` — Contains the user's login password.
* `machineResult` — begins uninitialized, but is later used to store a response that will be printed to the output panel, letting the user know whether the machine is switched on.
* `pwdResult` — begins uninitialized, but is later used to store a response that will be printed to the output panel, letting the user know whether their login attempt was successful.

We'd like you to create an `if...else` structure that checks whether the machine is switched on and puts a message into the `machineResult` variable telling the user whether it is on or off.

If the machine is on, we also want a second conditional to run that checks whether the `pwd` is equal to `cheese`. If so, it should assign a string to `pwdResult` telling the user they logged in successfully. If not, it should assign a different string to `pwdResult` telling the user their login attempt was not successful. We'd like you to do this in a single line, using something that isn't an `if...else` structure.

Try updating the live code below to recreate the finished example:

Download the starting point for this task to work in your own editor or in an online editor.


Test your skills: Loops - Learn web development

Test your skills: Loops
=======================

The aim of this skill test is to assess whether you've understood our Looping code article.

**Note:** You can try solutions by downloading the code and putting it in an online editor such as CodePen, JSFiddle, or Glitch.

If you get stuck, you can reach out to us in one of our communication channels.

DOM manipulation: considered useful
-----------------------------------

Some of the questions below require you to write some DOM manipulation code to complete them — such as creating new HTML elements, setting their text contents to equal specific string values, and nesting them inside existing elements on the page — all via JavaScript.

We haven't explicitly taught this yet in the course, but you'll have seen some examples that make use of it, and we'd like you to do some research into what DOM APIs you need to successfully answer the questions. A good starting place is our Manipulating documents tutorial.

Loops 1
-------

In our first looping task we want you to start by creating a simple loop that goes through all the items in the provided `myArray` and prints them out on the screen inside list items (i.e., `<li>` elements), which are appended to the provided `list`.

Download the starting point for this task to work in your own editor or in an online editor.

Loops 2
-------

In this next task, we want you to write a simple program that, given a name, searches an array of objects containing names and phone numbers (`phonebook`) and, if it finds the name, outputs the name and phone number into the paragraph (`para`) and then exits the loop before it has run its course.

If you haven't read about objects yet, don't worry! For now, all you need to know is how to access a member-value pair. You can read up on objects in the JavaScript object basics tutorial.

You are given three variables to begin with:

* `name` — contains a name to search for
* `para` — contains a reference to a paragraph, which will be used to report the results
* `phonebook` - contains the phonebook entries to search.

You should use a type of loop that you've not used in the previous task.

Download the starting point for this task to work in your own editor or in an online editor.

Loops 3
-------

In this final task, you are provided with the following:

* `i` — starts off with a value of 500; intended to be used as an iterator.
* `para` — contains a reference to a paragraph, which will be used to report the results.
* `isPrime()` — a function that, when passed a number, returns `true` if the number is a prime number, and `false` if not.

You need to use a loop to go through the numbers 2 to 500 backwards (1 is not counted as a prime number), and run the provided `isPrime()` function on them. For each number that isn't a prime number, continue on to the next loop iteration. For each one that is a prime number, add it to the paragraph's `textContent` along with some kind of separator.

You should use a type of loop that you've not used in the previous two tasks.

Download the starting point for this task to work in your own editor or in an online editor.


How to use promises - Learn web development

How to use promises
===================

* Previous
* Overview: Asynchronous
* Next

**Promises** are the foundation of asynchronous programming in modern JavaScript. A promise is an object returned by an asynchronous function, which represents the current state of the operation. At the time the promise is returned to the caller, the operation often isn't finished, but the promise object provides methods to handle the eventual success or failure of the operation.

|  |  |
| --- | --- |
| Prerequisites: |
 A reasonable understanding of JavaScript
 fundamentals, including event handling.
  |
| Objective: | To understand how to use promises in JavaScript. |

In the previous article, we talked about the use of callbacks to implement asynchronous functions. With that design, you call the asynchronous function, passing in your callback function. The function returns immediately and calls your callback when the operation is finished.

With a promise-based API, the asynchronous function starts the operation and returns a `Promise` object. You can then attach handlers to this promise object, and these handlers will be executed when the operation has succeeded or failed.

Using the fetch() API
---------------------

**Note:** In this article, we will explore promises by copying code samples from the page into your browser's JavaScript console. To set this up:

1. open a browser tab and visit https://example.org
2. in that tab, open the JavaScript console in your browser's developer tools
3. when we show an example, copy it into the console. You will have to reload the page each time you enter a new example, or the console will complain that you have redeclared `fetchPromise`.

In this example, we'll download the JSON file from https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json, and log some information about it.

To do this, we'll make an **HTTP request** to the server. In an HTTP request, we send a request message to a remote server, and it sends us back a response. In this case, we'll send a request to get a JSON file from the server. Remember in the last article, where we made HTTP requests using the `XMLHttpRequest` API? Well, in this article, we'll use the `fetch()` API, which is the modern, promise-based replacement for `XMLHttpRequest`.

Copy this into your browser's JavaScript console:

```js
const fetchPromise = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);

console.log(fetchPromise);

fetchPromise.then((response) => {
  console.log(`Received response: ${response.status}`);
});

console.log("Started request…");
```

Here we are:

1. calling the `fetch()` API, and assigning the return value to the `fetchPromise` variable
2. immediately after, logging the `fetchPromise` variable. This should output something like: `Promise { <state>: "pending" }`, telling us that we have a `Promise` object, and it has a `state` whose value is `"pending"`. The `"pending"` state means that the fetch operation is still going on.
3. passing a handler function into the Promise's **`then()`** method. When (and if) the fetch operation succeeds, the promise will call our handler, passing in a `Response` object, which contains the server's response.
4. logging a message that we have started the request.

The complete output should be something like:
```

Promise { <state>: "pending" }
Started request…
Received response: 200
```

Note that `Started request…` is logged before we receive the response. Unlike a synchronous function, `fetch()` returns while the request is still going on, enabling our program to stay responsive. The response shows the `200` (OK) status code, meaning that our request succeeded.

This probably seems a lot like the example in the last article, where we added event handlers to the `XMLHttpRequest` object. Instead of that, we're passing a handler into the `then()` method of the returned promise.

Chaining promises
-----------------

With the `fetch()` API, once you get a `Response` object, you need to call another function to get the response data. In this case, we want to get the response data as JSON, so we would call the `json()` method of the `Response` object. It turns out that `json()` is also asynchronous. So this is a case where we have to call two successive asynchronous functions.

Try this:

```js
const fetchPromise = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);

fetchPromise.then((response) => {
  const jsonPromise = response.json();
  jsonPromise.then((data) => {
    console.log(data[0].name);
  });
});
```

In this example, as before, we add a `then()` handler to the promise returned by `fetch()`. But this time, our handler calls `response.json()`, and then passes a new `then()` handler into the promise returned by `response.json()`.

This should log "baked beans" (the name of the first product listed in "products.json").

But wait! Remember the last article, where we said that by calling a callback inside another callback, we got successively more nested levels of code? And we said that this "callback hell" made our code hard to understand? Isn't this just the same, only with `then()` calls?

It is, of course. But the elegant feature of promises is that *`then()` itself returns a promise, which will be completed with the result of the function passed to it*. This means that we can (and certainly should) rewrite the above code like this:

```js
const fetchPromise = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);

fetchPromise
  .then((response) => response.json())
  .then((data) => {
    console.log(data[0].name);
  });
```

Instead of calling the second `then()` inside the handler for the first `then()`, we can *return* the promise returned by `json()`, and call the second `then()` on that return value. This is called **promise chaining** and means we can avoid ever-increasing levels of indentation when we need to make consecutive asynchronous function calls.

Before we move on to the next step, there's one more piece to add. We need to check that the server accepted and was able to handle the request, before we try to read it. We'll do this by checking the status code in the response and throwing an error if it wasn't "OK":

```js
const fetchPromise = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);

fetchPromise
  .then((response) => {
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    return response.json();
  })
  .then((data) => {
    console.log(data[0].name);
  });
```

Catching errors
---------------

This brings us to the last piece: how do we handle errors? The `fetch()` API can throw an error for many reasons (for example, because there was no network connectivity or the URL was malformed in some way) and we are throwing an error ourselves if the server returned an error.

In the last article, we saw that error handling can get very difficult with nested callbacks, making us handle errors at every nesting level.

To support error handling, `Promise` objects provide a `catch()` method. This is a lot like `then()`: you call it and pass in a handler function. However, while the handler passed to `then()` is called when the asynchronous operation *succeeds*, the handler passed to `catch()` is called when the asynchronous operation *fails*.

If you add `catch()` to the end of a promise chain, then it will be called when any of the asynchronous function calls fail. So you can implement an operation as several consecutive asynchronous function calls, and have a single place to handle all errors.

Try this version of our `fetch()` code. We've added an error handler using `catch()`, and also modified the URL so the request will fail.

```js
const fetchPromise = fetch(
  "bad-scheme://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);

fetchPromise
  .then((response) => {
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    return response.json();
  })
  .then((data) => {
    console.log(data[0].name);
  })
  .catch((error) => {
    console.error(`Could not get products: ${error}`);
  });
```

Try running this version: you should see the error logged by our `catch()` handler.

Promise terminology
-------------------

Promises come with some quite specific terminology that it's worth getting clear about.

First, a promise can be in one of three states:

* **pending**: the promise has been created, and the asynchronous function it's associated with has not succeeded or failed yet. This is the state your promise is in when it's returned from a call to `fetch()`, and the request is still being made.
* **fulfilled**: the asynchronous function has succeeded. When a promise is fulfilled, its `then()` handler is called.
* **rejected**: the asynchronous function has failed. When a promise is rejected, its `catch()` handler is called.

Note that what "succeeded" or "failed" means here is up to the API in question. For example, `fetch()` rejects the returned promise if (among other reasons) a network error prevented the request being sent, but fulfills the promise if the server sent a response, even if the response was an error like 404 Not Found.

Sometimes, we use the term **settled** to cover both **fulfilled** and **rejected**.

A promise is **resolved** if it is settled, or if it has been "locked in" to follow the state of another promise.

The article Let's talk about how to talk about promises gives a great explanation of the details of this terminology.

Combining multiple promises
---------------------------

The promise chain is what you need when your operation consists of several asynchronous functions, and you need each one to complete before starting the next one. But there are other ways you might need to combine asynchronous function calls, and the `Promise` API provides some helpers for them.

Sometimes, you need all the promises to be fulfilled, but they don't depend on each other. In a case like that, it's much more efficient to start them all off together, then be notified when they have all fulfilled. The `Promise.all()` method is what you need here. It takes an array of promises and returns a single promise.

The promise returned by `Promise.all()` is:

* fulfilled when and if *all* the promises in the array are fulfilled. In this case, the `then()` handler is called with an array of all the responses, in the same order that the promises were passed into `all()`.
* rejected when and if *any* of the promises in the array are rejected. In this case, the `catch()` handler is called with the error thrown by the promise that rejected.

For example:

```js
const fetchPromise1 = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);
const fetchPromise2 = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/not-found",
);
const fetchPromise3 = fetch(
  "https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json",
);

Promise.all([fetchPromise1, fetchPromise2, fetchPromise3])
  .then((responses) => {
    for (const response of responses) {
      console.log(`${response.url}: ${response.status}`);
    }
  })
  .catch((error) => {
    console.error(`Failed to fetch: ${error}`);
  });
```

Here, we're making three `fetch()` requests to three different URLs. If they all succeed, we will log the response status of each one. If any of them fail, then we're logging the failure.

With the URLs we've provided, all the requests should be fulfilled, although for the second, the server will return `404` (Not Found) instead of `200` (OK) because the requested file does not exist. So the output should be:
```

https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json: 200
https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/not-found: 404
https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json: 200
```

If we try the same code with a badly formed URL, like this:

```js
const fetchPromise1 = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);
const fetchPromise2 = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/not-found",
);
const fetchPromise3 = fetch(
  "bad-scheme://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json",
);

Promise.all([fetchPromise1, fetchPromise2, fetchPromise3])
  .then((responses) => {
    for (const response of responses) {
      console.log(`${response.url}: ${response.status}`);
    }
  })
  .catch((error) => {
    console.error(`Failed to fetch: ${error}`);
  });
```

Then we can expect the `catch()` handler to run, and we should see something like:
```

Failed to fetch: TypeError: Failed to fetch
```

Sometimes, you might need any one of a set of promises to be fulfilled, and don't care which one. In that case, you want `Promise.any()`. This is like `Promise.all()`, except that it is fulfilled as soon as any of the array of promises is fulfilled, or rejected if all of them are rejected:

```js
const fetchPromise1 = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
);
const fetchPromise2 = fetch(
  "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/not-found",
);
const fetchPromise3 = fetch(
  "https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json",
);

Promise.any([fetchPromise1, fetchPromise2, fetchPromise3])
  .then((response) => {
    console.log(`${response.url}: ${response.status}`);
  })
  .catch((error) => {
    console.error(`Failed to fetch: ${error}`);
  });
```

Note that in this case we can't predict which fetch request will complete first.

These are just two of the extra `Promise` functions for combining multiple promises. To learn about the rest, see the `Promise` reference documentation.

async and await
---------------

The `async` keyword gives you a simpler way to work with asynchronous promise-based code. Adding `async` at the start of a function makes it an async function:

```js
async function myFunction() {
  // This is an async function
}
```

Inside an async function, you can use the `await` keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.

This enables you to write code that uses asynchronous functions but looks like synchronous code. For example, we could use it to rewrite our fetch example:

```js
async function fetchProducts() {
  try {
    // after this line, our function will wait for the `fetch()` call to be settled
    // the `fetch()` call will either return a Response or throw an error
    const response = await fetch(
      "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
    );
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    // after this line, our function will wait for the `response.json()` call to be settled
    // the `response.json()` call will either return the parsed JSON object or throw an error
    const data = await response.json();
    console.log(data[0].name);
  } catch (error) {
    console.error(`Could not get products: ${error}`);
  }
}

fetchProducts();
```

Here, we are calling `await fetch()`, and instead of getting a `Promise`, our caller gets back a fully complete `Response` object, just as if `fetch()` were a synchronous function!

We can even use a `try...catch` block for error handling, exactly as we would if the code were synchronous.

Note though that async functions always return a promise, so you can't do something like:

```js
async function fetchProducts() {
  try {
    const response = await fetch(
      "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
    );
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error(`Could not get products: ${error}`);
  }
}

const promise = fetchProducts();
console.log(promise[0].name); // "promise" is a Promise object, so this will not work
```

Instead, you'd need to do something like:

```js
async function fetchProducts() {
  try {
    const response = await fetch(
      "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
    );
    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error(`Could not get products: ${error}`);
  }
}

const promise = fetchProducts();
promise.then((data) => console.log(data[0].name));
```

Also, note that you can only use `await` inside an `async` function, unless your code is in a JavaScript module. That means you can't do this in a normal script:

```js
try {
  // using await outside an async function is only allowed in a module
  const response = await fetch(
    "https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/products.json",
  );
  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }
  const data = await response.json();
  console.log(data[0].name);
} catch (error) {
  console.error(`Could not get products: ${error}`);
}
```

You'll probably use `async` functions a lot where you might otherwise use promise chains, and they make working with promises much more intuitive.

Keep in mind that just like a promise chain, `await` forces asynchronous operations to be completed in series. This is necessary if the result of the next operation depends on the result of the last one, but if that's not the case then something like `Promise.all()` will be more performant.

Conclusion
----------

Promises are the foundation of asynchronous programming in modern JavaScript. They make it easier to express and reason about sequences of asynchronous operations without deeply nested callbacks, and they support a style of error handling that is similar to the synchronous `try...catch` statement.

The `async` and `await` keywords make it easier to build an operation from a series of consecutive asynchronous function calls, avoiding the need to create explicit promise chains, and allowing you to write code that looks just like synchronous code.

Promises work in the latest versions of all modern browsers; the only place where promise support will be a problem is in Opera Mini and IE11 and earlier versions.

We didn't touch on all features of promises in this article, just the most interesting and useful ones. As you start to learn more about promises, you'll come across more features and techniques.

Many modern Web APIs are promise-based, including WebRTC, Web Audio API, Media Capture and Streams API, and many more.

See also
--------

* `Promise()`
* Using promises
* We have a problem with promises by Nolan Lawson
* Let's talk about how to talk about promises
* Previous
* Overview: Asynchronous
* Next


Introducing workers - Learn web development

Introducing workers
===================

* Previous
* Overview: Asynchronous
* Next

In this final article in our "Asynchronous JavaScript" module, we'll introduce *workers*, which enable you to run some tasks in a separate thread of execution.

|  |  |
| --- | --- |
| Prerequisites: |
 A reasonable understanding of JavaScript
 fundamentals, including event handling.
  |
| Objective: | To understand how to use web workers. |

In the first article of this module, we saw what happens when you have a long-running synchronous task in your program — the whole window becomes totally unresponsive. Fundamentally, the reason for this is that the program is *single-threaded*. A *thread* is a sequence of instructions that a program follows. Because the program consists of a single thread, it can only do one thing at a time: so if it is waiting for our long-running synchronous call to return, it can't do anything else.

Workers give you the ability to run some tasks in a different thread, so you can start the task, then continue with other processing (such as handling user actions).

 One concern from all this is that if multiple threads can have access to the same shared data, it's possible for them to change it independently and unexpectedly (with respect to each other).
 This can cause bugs that are hard to find.

 To avoid these problems on the web, your main code and your worker code never get direct access to each other's variables, and can only truly "share" data in very specific cases.
 Workers and the main code run in completely separate worlds, and only interact by sending each other messages. In particular, this means that workers can't access the DOM (the window, document, page elements, and so on).

There are three different sorts of workers:

* dedicated workers
* shared workers
* service workers

In this article, we'll walk through an example of the first sort of worker, then briefly discuss the other two.

Using web workers
-----------------

Remember in the first article, where we had a page that calculated prime numbers? We're going to use a worker to run the prime-number calculation, so our page stays responsive to user actions.

### The synchronous prime generator

Let's first take another look at the JavaScript in our previous example:

```js
function generatePrimes(quota) {
  function isPrime(n) {
    for (let c = 2; c <= Math.sqrt(n); ++c) {
      if (n % c === 0) {
        return false;
      }
    }
    return true;
  }

  const primes = [];
  const maximum = 1000000;

  while (primes.length < quota) {
    const candidate = Math.floor(Math.random() \* (maximum + 1));
    if (isPrime(candidate)) {
      primes.push(candidate);
    }
  }

  return primes;
}

document.querySelector("#generate").addEventListener("click", () => {
  const quota = document.querySelector("#quota").value;
  const primes = generatePrimes(quota);
  document.querySelector("#output").textContent =
    `Finished generating ${quota} primes!`;
});

document.querySelector("#reload").addEventListener("click", () => {
  document.querySelector("#user-input").value =
    'Try typing in here immediately after pressing "Generate primes"';
  document.location.reload();
});
```

In this program, after we call `generatePrimes()`, the program becomes totally unresponsive.

### Prime generation with a worker

For this example, start by making a local copy of the files at https://github.com/mdn/learning-area/blob/main/javascript/asynchronous/workers/start. There are four files in this directory:

* index.html
* style.css
* main.js
* generate.js

The "index.html" file and the "style.css" files are already complete:

```html
<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>Prime numbers</title>
    <script src="main.js" defer></script>
    <link href="style.css" rel="stylesheet" />
  </head>

  <body>
    <label for="quota">Number of primes:</label>
    <input type="text" id="quota" name="quota" value="1000000" />

    <button id="generate">Generate primes</button>
    <button id="reload">Reload</button>

    <textarea id="user-input" rows="5" cols="62">
Try typing in here immediately after pressing "Generate primes"
    </textarea>

    <div id="output"></div>
  </body>
</html>
```

```css
textarea {
  display: block;
  margin: 1rem 0;
}
```

The "main.js" and "generate.js" files are empty. We're going to add the main code to "main.js", and the worker code to "generate.js".

So first, we can see that the worker code is kept in a separate script from the main code. We can also see, looking at "index.html" above, that only the main code is included in a `<script>` element.

Now copy the following code into "main.js":

```js
// Create a new worker, giving it the code in "generate.js"
const worker = new Worker("./generate.js");

// When the user clicks "Generate primes", send a message to the worker.
// The message command is "generate", and the message also contains "quota",
// which is the number of primes to generate.
document.querySelector("#generate").addEventListener("click", () => {
  const quota = document.querySelector("#quota").value;
  worker.postMessage({
    command: "generate",
    quota,
  });
});

// When the worker sends a message back to the main thread,
// update the output box with a message for the user, including the number of
// primes that were generated, taken from the message data.
worker.addEventListener("message", (message) => {
  document.querySelector("#output").textContent =
    `Finished generating ${message.data} primes!`;
});

document.querySelector("#reload").addEventListener("click", () => {
  document.querySelector("#user-input").value =
    'Try typing in here immediately after pressing "Generate primes"';
  document.location.reload();
});
```

* First, we're creating the worker using the `Worker()` constructor. We pass it a URL pointing to the worker script. As soon as the worker is created, the worker script is executed.
* Next, as in the synchronous version, we add a `click` event handler to the "Generate primes" button. But now, rather than calling a `generatePrimes()` function, we send a message to the worker using `worker.postMessage()`. This message can take an argument, and in this case, we're passing a JSON object containing two properties:
    + `command`: a string identifying the thing we want the worker to do (in case our worker could do more than one thing)
    + `quota`: the number of primes to generate.
* Next, we add a `message` event handler to the worker. This is so the worker can tell us when it has finished, and pass us any resulting data. Our handler takes the data from the `data` property of the message, and writes it to the output element (the data is exactly the same as `quota`, so this is a bit pointless, but it shows the principle).
* Finally, we implement the `click` event handler for the "Reload" button. This is exactly the same as in the synchronous version.

Now for the worker code. Copy the following code into "generate.js":

```js
// Listen for messages from the main thread.
// If the message command is "generate", call `generatePrimes()`
addEventListener("message", (message) => {
  if (message.data.command === "generate") {
    generatePrimes(message.data.quota);
  }
});

// Generate primes (very inefficiently)
function generatePrimes(quota) {
  function isPrime(n) {
    for (let c = 2; c <= Math.sqrt(n); ++c) {
      if (n % c === 0) {
        return false;
      }
    }
    return true;
  }

  const primes = [];
  const maximum = 1000000;

  while (primes.length < quota) {
    const candidate = Math.floor(Math.random() \* (maximum + 1));
    if (isPrime(candidate)) {
      primes.push(candidate);
    }
  }

  // When we have finished, send a message to the main thread,
  // including the number of primes we generated.
  postMessage(primes.length);
}
```

Remember that this runs as soon as the main script creates the worker.

The first thing the worker does is start listening for messages from the main script. It does this using `addEventListener()`, which is a global function in a worker. Inside the `message` event handler, the `data` property of the event contains a copy of the argument passed from the main script. If the main script passed the `generate` command, we call `generatePrimes()`, passing in the `quota` value from the message event.

The `generatePrimes()` function is just like the synchronous version, except instead of returning a value, we send a message to the main script when we are done. We use the `postMessage()` function for this, which like `addEventListener()` is a global function in a worker. As we already saw, the main script is listening for this message and will update the DOM when the message is received.

**Note:** To run this site, you'll have to run a local web server, because file:// URLs are not allowed to load workers. See our guide to setting up a local testing server. With that done, you should be able to click "Generate primes" and have your main page stay responsive.

If you have any problems creating or running the example, you can review the finished version and try it live.

Other types of workers
----------------------

The worker we just created was what's called a *dedicated worker*. This means it's used by a single script instance.

There are other types of workers, though:

* *Shared workers* can be shared by several different scripts running in different windows.
* *Service workers* act like proxy servers, caching resources so that web applications can work when the user is offline. They're a key component of Progressive Web Apps.
Conclusion
----------

In this article we've introduced web workers, which enable a web application to offload tasks to a separate thread. The main thread and the worker don't directly share any variables, but communicate by sending messages, which are received by the other side as `message` events.

Workers can be an effective way to keep the main application responsive, although they can't access all the APIs that the main application can, and in particular can't access the DOM.

See also
--------

* Using web workers
* Using service workers
* Web workers API
* Previous
* Overview: Asynchronous
* Next


How to implement a promise-based API - Learn web development

How to implement a promise-based API
====================================

* Previous
* Overview: Asynchronous
* Next

In the last article we discussed how to use APIs that return promises. In this article we'll look at the other side — how to *implement* APIs that return promises. This is a much less common task than using promise-based APIs, but it's still worth knowing about.

|  |  |
| --- | --- |
| Prerequisites: |
 A reasonable understanding of JavaScript
 fundamentals, including event handling and the basics of promises.
  |
| Objective: | To understand how to implement promise-based APIs. |

Generally, when you implement a promise-based API, you'll be wrapping an asynchronous operation, which might use events, or plain callbacks, or a message-passing model. You'll arrange for a `Promise` object to handle the success or failure of that operation properly.

Implementing an alarm() API
---------------------------

In this example we'll implement a promise-based alarm API, called `alarm()`. It will take as arguments the name of the person to wake up and a delay in milliseconds to wait before waking the person up. After the delay, the function will send a "Wake up!" message, including the name of the person we need to wake up.

### Wrapping setTimeout()

We'll use the `setTimeout()` API to implement our `alarm()` function. The `setTimeout()` API takes as arguments a callback function and a delay, given in milliseconds. When `setTimeout()` is called, it starts a timer set to the given delay, and when the time expires, it calls the given function.

In the example below, we call `setTimeout()` with a callback function and a delay of 1000 milliseconds:

```html
<button id="set-alarm">Set alarm</button>
<div id="output"></div>
```

```
div {
  margin: 0.5rem 0;
}
```

```js
const output = document.querySelector("#output");
const button = document.querySelector("#set-alarm");

function setAlarm() {
  setTimeout(() => {
    output.textContent = "Wake up!";
  }, 1000);
}

button.addEventListener("click", setAlarm);
```

### The Promise() constructor

Our `alarm()` function will return a `Promise` that is fulfilled when the timer expires. It will pass a "Wake up!" message into the `then()` handler, and will reject the promise if the caller supplies a negative delay value.

The key component here is the `Promise()` constructor. The `Promise()` constructor takes a single function as an argument. We'll call this function the `executor`. When you create a new promise you supply the implementation of the executor.

This executor function itself takes two arguments, which are both also functions, and which are conventionally called `resolve` and `reject`. In your executor implementation, you call the underlying asynchronous function. If the asynchronous function succeeds, you call `resolve`, and if it fails, you call `reject`. If the executor function throws an error, `reject` is called automatically. You can pass a single parameter of any type into `resolve` and `reject`.

So we can implement `alarm()` like this:

```js
function alarm(person, delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) {
      throw new Error("Alarm delay must not be negative");
    }
    setTimeout(() => {
      resolve(`Wake up, ${person}!`);
    }, delay);
  });
}
```

This function creates and returns a new `Promise`. Inside the executor for the promise, we:

* check that `delay` is not negative, and throw an error if it is.
* call `setTimeout()`, passing a callback and `delay`. The callback will be called when the timer expires, and in the callback we call `resolve`, passing in our `"Wake up!"` message.
Using the alarm() API
---------------------

This part should be quite familiar from the last article. We can call `alarm()`, and on the returned promise call `then()` and `catch()` to set handlers for promise fulfillment and rejection.
```

<div>
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" size="4" value="Matilda" />
</div>

<div>
  <label for="delay">Delay:</label>
  <input type="text" id="delay" name="delay" size="4" value="1000" />
</div>

<button id="set-alarm">Set alarm</button>
<div id="output"></div>
```

```
button {
  display: block;
}

div,
button {
  margin: 0.5rem 0;
}
```

```js
const name = document.querySelector("#name");
const delay = document.querySelector("#delay");
const button = document.querySelector("#set-alarm");
const output = document.querySelector("#output");

function alarm(person, delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) {
      throw new Error("Alarm delay must not be negative");
    }
    setTimeout(() => {
      resolve(`Wake up, ${person}!`);
    }, delay);
  });
}

button.addEventListener("click", () => {
  alarm(name.value, delay.value)
    .then((message) => (output.textContent = message))
    .catch((error) => (output.textContent = `Couldn't set alarm: ${error}`));
});
```

Try setting different values for "Name" and "Delay". Try setting a negative value for "Delay".

Using async and await with the alarm() API
------------------------------------------

Since `alarm()` returns a `Promise`, we can do everything with it that we could do with any other promise: promise chaining, `Promise.all()`, and `async` / `await`:
```

<div>
  <label for="name">Name:</label>
  <input type="text" id="name" name="name" size="4" value="Matilda" />
</div>

<div>
  <label for="delay">Delay:</label>
  <input type="text" id="delay" name="delay" size="4" value="1000" />
</div>

<button id="set-alarm">Set alarm</button>
<div id="output"></div>
```

```
button {
  display: block;
}

div,
button {
  margin: 0.5rem 0;
}
```

```js
const name = document.querySelector("#name");
const delay = document.querySelector("#delay");
const button = document.querySelector("#set-alarm");
const output = document.querySelector("#output");

function alarm(person, delay) {
  return new Promise((resolve, reject) => {
    if (delay < 0) {
      throw new Error("Alarm delay must not be negative");
    }
    setTimeout(() => {
      resolve(`Wake up, ${person}!`);
    }, delay);
  });
}

button.addEventListener("click", async () => {
  try {
    const message = await alarm(name.value, delay.value);
    output.textContent = message;
  } catch (error) {
    output.textContent = `Couldn't set alarm: ${error}`;
  }
});
```

See also
--------

* `Promise()` constructor
* Using promises
* Previous
* Overview: Asynchronous
* Next


Sequencing animations - Learn web development

Sequencing animations
=====================

* Previous
* Overview: Asynchronous

In this assessment you'll update a page to play a series of animations in a sequence. To do this you'll use some of the techniques we learned in the How to use Promises article.

|  |  |
| --- | --- |
| Prerequisites: |
 A reasonable understanding of JavaScript
 fundamentals, how to use promise-based APIs.
  |
| Objective: | To test understanding of how to use promise-based APIs. |

Starting point
--------------

Make a local copy of the files at https://github.com/mdn/learning-area/tree/main/javascript/asynchronous/sequencing-animations/start. It contains four files:

* alice.svg
* index.html
* main.js
* style.css

The only file you'll need to edit is "main.js".

If you open "index.html" in a browser you'll see three images arranged diagonally:

![Screenshot of sequencing-animations assessment page](/en-US/docs/Learn/JavaScript/Asynchronous/Sequencing_animations/sequencing-animations.png)

The images are taken from our guide to Using the Web Animations API.

**Note:** If you get stuck, you can reach out to us in one of our communication channels.

Project brief
-------------

We want to update this page so we apply an animation to all three images, one after the other. So when the first has finished we animate the second, and when the second has finished we animate the third.

The animation is already defined in "main.js": it just rotates the image and shrinks it until it disappears.

To give you more of an idea of how we want the page to work, have a look at the finished example. Note that the animations only run once: to see them run again, reload the page.

Steps to complete
-----------------

### Animating the first image

We're using the Web Animations API to animate the images, specifically the `element.animate()` method.

Update "main.js" to add a call to `alice1.animate()`, like this:

```js
const aliceTumbling = [
  { transform: "rotate(0) scale(1)" },
  { transform: "rotate(360deg) scale(0)" },
];

const aliceTiming = {
  duration: 2000,
  iterations: 1,
  fill: "forwards",
};

const alice1 = document.querySelector("#alice1");
const alice2 = document.querySelector("#alice2");
const alice3 = document.querySelector("#alice3");

alice1.animate(aliceTumbling, aliceTiming);
```

Reload the page, and you should see the first image rotate and shrink.

### Animating all the images

Next, we want to animate `alice2` when `alice1` has finished, and `alice3` when `alice2` has finished.

The `animate()` method returns an `Animation` object. This object has a `finished` property, which is a `Promise` that is fulfilled when the animation has finished playing. So we can use this promise to know when to start the next animation.

We'd like you to try a few different ways to implement this, to reinforce different ways of using promises.

1. First, implement something that works, but has the promise version of the "callback hell" problem we saw in our discussion of using callbacks.
2. Next, implement it as a promise chain. Note that there are a few different ways you can write this, because of the different forms you can use for an arrow function. Try some different forms. Which is the most concise? Which do you find the most readable?
3. Finally, implement it using `async` and `await`.

Remember that `element.animate()` does *not* return a `Promise`: it returns an `Animation` object with a `finished` property that is a `Promise`.

* Previous
* Overview: Asynchronous