COMP3512

winter 2023

lec-20

the

doneometer

what's ahead?

⚠️

⚠️

⚠️

some truths (I think)

asynchronous coding is hard

asynchronous coding is confusing

but

...as long as nothing goes south

you can follow some reasonably simple "recipes" to make things work...

we'll take things slow and experimental today...

...and you'll have some "recipes" that will move you forward with The Project

🤔What general steps do we need to take in our JS code to make this happen?

DEMO
1-fetch

let's do an extended demo

Say we want to place some image ids in a list on a page when a button is clicked, but the ids are only accessible from an API endpoint.

setting the scene

DEMO
1-fetch

our simple API endpoint

<?php

header('Content-Type: application/json');

echo json_encode([13, 121, 2, 17, 42]);

🤔What response do we get if we go to the endpoint URL?

🤔 BTW: how can you confirm you're returning valid JSON?

DEMO
1-fetch

consuming that endpoint - a first attempt

let fetchResult = fetch("");

console.log(fetchResult);

🤔What should our argument to fetch() be?

🤔What will be logged? What is fetchResult? Is this surprising?

🤔What is coming across the network? Where could we see that?

DEMO
1-fetch

huh.

🤔What's up with the           ?

🤔What are these [[blahblah]] things?

🤔What is "pending" and "fulfilled" telling us?

DEMO
1-fetch

opening up the Promise

🤔What methods do these Promise objects have?

DEMO
1-fetch

🤔 In the [[PromiseResult]], what is json? What does it return?

opening up the PromiseResult

so what have we discovered?

...that fetch() returns some weird thing called a Promise that contains a whole bunch of properties...and that the thing we actually want - the JSON from the fetch - isn't even directly available from this beast.

...that this Promise object seems to have (at least) 2 states: pending and fulfilled.

Wonderful.

...that we can consume an API endpoint with fetch()

But wait!
There's more!

Let's tell our API to be a sleepyhead.

<?php

header('Content-Type: application/json');

sleep(10); // sleep for 10 seconds

echo json_encode([13, 121, 2, 17, 42]);

🤔 Predict how this will affect what we see in the console.

🤔 What happens when we expand the Promise this time?

🤔 What happens if we wait 10 seconds and THEN expand?

🤔 What's going on in Fetch/XHR land this time?

🤔 What in the name of all that is holy is going on?!?!

JS is single-threaded...it just executes a script one line at a time, fully completing each line of code before moving on to the next.

But that contradicts what we're seeing here, doesn't it? That console.log is happening right away, despite the endpoint sleeping for 10 seconds!

let fetchResult = fetch("../api/images.php");

console.log(fetchResult);

fetch() immediately returns a Promise object, which goes into fetchResult, and...

A two-pronged statement

...the browser goes to work actually grabbing the data, hopefully eventually fulfilling the Promise

so how do you get the stuff "in" the Promise?

DEMO
2-fetch-json
// http://127.0.0.1:8080/2-fetch-json/index.html

let response = await fetch("/api/images.php");
console.log("line 4:", response);

let ids = await response.json();
console.log("line 7:", ids);
DEMO
2-fetch-json

🤔Predict what will appear in the console.

🤔What does await seem to be doing?

🤔What would happen if we removed type="module" from our <script>?

using await and json() w/ fetch()

🤔So can I only use await in a module?

A Project Hint

🤔 What do we want to do when the user-facing page loads?

Some reminders:

There are some things you need to put into local storage if they aren't there already. What are they?

What should the user see on the page when they arrive?

🤔 How can we do this using a single <script> file?

🤔 Should you do this with a single <script> file?

BRAIN
BREAK

async functions

A reminder - we are trying to build an event handler.

That means we're building a function...so we can't just "use a script with awaits" here.

Cue the music for...

maybe we can build a function that gets the ids we want via the fetch?

// http://127.0.0.1:8080/3-async-await/index.html

async function picIds() {
  let response = await fetch("../api/images.php");
  let ids = await response.json();
  return ids;
}

let ids = picIds();
console.log("The ids are:", ids);

this looks reasonable

🤔 Predict what will be displayed in the console.

note async

🤬

DEMO
3-async-await-trace

let's dig

// http://127.0.0.1:8080/3-async-await/index.html

async function picIds() {
  console.log("line 4:", "picIds starts");

  let response = await fetch("/api/images.php");
  console.log("line 7:", response.constructor.name);

  let ids = await response.json();
  console.log("line 10:", ids.constructor.name);

  return ids;
}

let ids = picIds();
console.log("line 16:", ids.constructor.name);

console.log("Line 18:", ids);

neat trick

🤔Predict what will appear in the console. Does this seem like quiz fodder?

DEMO
3-async-await

let's unpack this

// http://127.0.0.1:8080/3-async-await/index.html

async function picIds() {
  console.log("line 4:", "picIds starts");

  let response = await fetch("/api/images.php");
  console.log("line 7:", response.constructor.name);

  let ids = await response.json();
  console.log("line 10:", ids.constructor.name);

  return ids;
}

let ids = picIds();
console.log("line 16:", ids.constructor.name);

console.log("Line 18:", ids);

1. the script loads

2. line 15 runs, calling picIds()

3. line 4 runs

4. line 6 runs...but since there's an await, JS punts us out of the function! 

5. line 15 stores the ???

PSA: I have to do an "Empty Cache and Hard Reload" in the browser sometimes when async/await is involved. Just sayin'.

6. line 16 runs

7. line 18 runs. JS is now done running SYNCHRONOUS CODE, so will give "paused" ASYNC CODE a chance

8. line 6 picks up where it left off

9. line 7 runs

10. line 9 runs...the "pause" happens again, but again - no sync code, so back in we go

11. line 10 runs

12. line 12 returns an Array...but it's too late!

DEMO
3-async-await

let's unpack this

// http://127.0.0.1:8080/3-async-await/index.html

async function picIds() {
  console.log("line 4:", "picIds starts");

  let response = await fetch("/api/images.php");
  console.log("line 7:", response.constructor.name);

  let ids = await response.json();
  console.log("line 10:", ids.constructor.name);

  return ids;
}

let ids = picIds();
console.log("line 16:", ids.constructor.name);

console.log("Line 18:", ids);

1. the script loads

2. line 15 runs, calling picIds()

3. line 4 runs

4. line 6 runs...but since there's an await, JS punts us out of the function! 

5. line 15 stores the ???

PSA: I have to do an "Empty Cache and Hard Reload" in the browser sometimes when async/await is involved. Just sayin'.

6. line 16 runs

7. line 18 runs. JS is now done running SYNCHRONOUS CODE, so will give "paused" ASYNC CODE a chance

8. line 6 picks up where it left off

9. line 7 runs

10. line 9 runs...the "pause" happens again, but again - no sync code, so back in we go

11. line 10 runs

12. line 12 returns an Array...but it's too late!

ow

ANOTHER BRAIN BREAK
('cause ow)

DEMO
3-async-await

how can we fix this?

// http://127.0.0.1:8080/2-fetch/index.html

async function picIds() {
  console.log("line 4:", "picIds starts");

  let response = await fetch("/api/images.php");
  console.log("line 7:", response.constructor.name);

  let ids = await response.json();
  console.log("line 10:", ids.constructor.name);

  return ids;
}

let ids = picIds();
console.log("line 16:", ids.constructor.name);

console.log("Line 18:", ids);

What does this return?

🤔What is picIds() returning?

🤔So how do we make line 15 wait?

whew

Another Project Hint

🤔 When a user clicks on a country name, what should they see?

🤔 What information from the DB is needed to make that happen?

🤔 How many API calls are needed to get that information?

🤔 Is there a way to make a number of API calls in parallel?

Promise.all

We'll talk about this on Monday, but if you are itching to get this done over this weekend, you can look at how it's used with async/await over on MDN. (the textbook doesn't talk about Promise.all in conjunction with async/await)

Made with Slides.com