Skip to content

Surprise JavaScript Asynchronous

(aka Asynchronous JavaScript Surprise)

Overview

In this lab, you’ll work with asynchronous JavaScript to understand how callbacks and Promises work. You’ll implement three different buttons that demonstrate asynchronous patterns.

Learning Objectives

  • Understand how setTimeout works with callbacks
  • Work with random timing in asynchronous operations
  • Use the fetch API with Promises
  • Handle JSON responses from APIs
  • Chain .then() methods for promise handling

Getting Started

Download surprise.html and put it in your cs343 folder, in a folder called surprise.

Open it in VS Code. You’ll see three buttons already set up:

  • “Call me in 5”
  • “Surprise me”
  • “Dog? Dog.”

Your job is to implement the three functions that make these buttons work.

Part 1: Call me in 5

Implement the callMeInFive() function:

  1. Display the text “Waiting…” in the display area immediately when the button is clicked.
  2. In the callback function, write code to change the display text to “Hi, how are you?”.
  3. setTimeout() lets you delay code to run after a certain amount of time:

    Key Concept: setTimeout

    setTimeout(callback, delay) executes the callback function after the specified delay in milliseconds.

    setTimeout(function() {
      // This code runs after the delay
    }, 5000);
    

Test it out! Open a preview of index.html in VS Code and click the “Call me in 5” button.

Part 2: Surprise Me

Now it’s your turn to try setTimeout() for yourself! Implement the surpriseMe() function:

  1. Display the text “Unaware…” in the display area immediately when the button is clicked.
  2. Generate a random delay between 1 and 10 seconds:
    • Use the provided getRandomNumber(min, max) helper function.
    • Don’t forget that setTimeout requires milliseconds.
  3. Use setTimeout() with your random delay to schedule a callback.
  4. In the callback function, change the display text to “Surprise!”.

Test it out! Click the “Surprise me” button and see if it works as expected.

Then, click the “Call me in 5” and then click the “Surprise me” button while you’re waiting for the first one to finish.

What happens?

Key Concept: Asynchronous Operations

Asynchronous operations (like setTimeout) allow your code to perform tasks without blocking others.

Clicking one button doesn’t block you from clicking another button. Each button’s behavior runs independently, even if they are both waiting for their respective timers to finish.

Part 3: Dog? Dog.

Asynchronous JavaScript is important for things that may take time to complete (like get data from a server/API).

The third button should go get some data from a server and do something with it. How? We’ll use the Fetch API.

Fetch API and Promises

The fetch() function is a simple way to make a network request and get data back.

However, because it may take time to get a response, fetch() doesn’t return the data directly. Instead, it returns a “Promise” that will eventually resolve to the response.

Implement the fetchDog() function:

  1. Display the text “Fetching…” in the display area immediately when the button is clicked.
  2. Use fetch() to make a request to the Dog API: https://dog.ceo/api/breeds/image/random

    • The argument to fetch() is a string containing the URL to fetch from.
    • Note that fetch() returns a Promise object, not the actual response data.
    • You can store the returned Promise in a variable, for example:
      const responsePromise = fetch(url);
      
  3. Call the .then() method on the Promise to handle the response once it’s ready:

    • The .then() method takes a callback function with one parameter: the response object, for example:

      responsePromise.then(function(response) {
          // Handle response here
      });
      
  4. The response object has text() and json() methods to get the response data as text or as a parsed JSON object, respectively. Let’s use json() to get the data as an object:

    • Inside the callback, add this line of code to parse the JSON:

          return response.json();
      
    • Here’s the problem: response.json() also returns a Promise for the parsed JSON data, so we can’t do anything with it yet.

      • However, if we return that Promise, then we can actually chain another .then()!
  5. Chain another .then() to handle the parsed JSON data like so:

    const responsePromise = fetch(url);
    
    responsePromise.then(function(response) {
        return response.json();
    })
    .then(function(data) {
        // Here, data will be the parsed JSON object
    });
    
    • What’s happening here? It’s exactly how it reads:
      1. First, fetch(url) makes the request and returns a Promise for the response.
      2. Each .then() specifies what code to run when the previous Promise finishes (“resolves”).
      3. The first .then() handles the response and returns response.json(), which is another Promise for the parsed JSON data.
      4. The second .then() handles the parsed JSON data coming from when response.json() resolves.
  6. Inside the second .then() callback:

    • The data object will be the JSON object returned by the API, for example:

      {
          "message": "https://images.dog.ceo/breeds/hound-afghan/n02088094_1003.jpg",
          "status": "success"
      }
      
    • Get the image URL from the object’s message property, then display the image in the display area.

      • You can use .innerHTML to replace everything in the display area with an <img> tag, for example:

        display.innerHTML = `<img src='${imageUrl}' alt='Random Dog Image' />`;
        

        (Generally, we avoid using .innerHTML for security reasons.)

Test it out! Click the “Dog? Dog.” button. You should see “Fetching…” and then after a moment see a random dog image.

Key Concept: Promises

Promises allow us to define callbacks to run once the response is ready, without blocking the rest of our code from running in the meantime.

Promises also allow you to chain asynchronous operations with .then(). Each .then() receives the result of the previous Promise, and can return a new Promise for the next step in the chain.

Try clicking multiple buttons in sequence to see how async operations work independently.

  • For example, click “Call me in 5” multiple times, then click “Surprise me” multiple times, and click “Dog? Dog.” every so often.
  • You should see how each click operates independently without blocking the others.

Challenges (Optional)

  • Add error handling to the fetchDog() function using .catch().
  • Have “Call me in 5” display a countdown before showing the final message. You’ll want to use setInterval() for this.
  • Add a fourth button that uses async/await instead of .then() chains.

Submission

Submit your completed surprise.html to the corresponding assignment.