Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change scheme for fetch #1307

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 0 additions & 39 deletions common-content/en/module/js3/asynchrony/index.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block now feels a bit mis-named - it's really talking about synchronous execution by way of setting up the next block to talk about asynchronous execution? May be worth either renaming, or combining the blocks?

Also if we keep this as-is, I'd drop its time from 40 minutes down to 5-10 minutes.

Original file line number Diff line number Diff line change
Expand Up @@ -44,42 +44,3 @@ A single thread can do one thing at a time. JavaScript is a single threaded lang
{{</tooltip>}}.

When we call a function, the function will run to completion before the next line of code is executed. But what if we need to wait for something to happen? What if we need to wait for our data to arrive before we can show it? In this case, we can use **asynchronous execution**.

### Event Loop

We have already used asynchronous execution. We have defined `eventListener`s that _listen_ for events to happen, _then_ execute a callback function.

```js
const search = document.getElementById("search");
search.addEventListener("input", handleInput);
```

When we called `addEventListener` it didn't immediately call `handleInput`.

But here's a new idea: eventListeners are part of the [Event API](https://developer.mozilla.org/en-US/docs/Web/API/Event). They are not part of the JavaScript language! 🤯 This means you can't use them in a Node REPL. But they are implemented in web browsers. The core of JavaScript (e.g. strings and functions) is the same everywhere, but different contexts may add extra APIs.

When you set an eventListener you are really sending a call to a Web API and asking it do something for you.

```js
const search = document.getElementById("search");
search.addEventListener("input", handleInput);
```

The callback `handleInput` does not run until the user types. With `fetch`, the callback function does not run until the data arrives. In both cases, we are waiting for something to happen before we can run our code.

We use a function as a way of wrapping up the code that needs to be run later on. This means we can tell the browser _what_ to do when we're done waiting.

**👉🏽 [Visualise the Event Loop](http://latentflip.com/loupe/?code=JC5vbignYnV0dG9uJywgJ2NsaWNrJywgZnVuY3Rpb24gb25DbGljaygpIHsKICAgIGNvbnNvbGUubG9nKCdZb3UgY2xpY2tlZCB0aGUgYnV0dG9uIScpOyAgICAKfSk7Cgpjb25zb2xlLmxvZygiSGkhIik7Cgpjb25zb2xlLmxvZygiV2VsY29tZSB0byB0aGUgZXZlbnQgbG9vcCIpOw%3D%3D!!!PGJ1dHRvbj5DbGljayBtZSE8L2J1dHRvbj4%3D)**

### 🧠 Recap our concept map
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've removed the map - can I have it back? Not everyone thinks in the same way. We can have multiple ways of modelling ideas.


```mermaid
graph LR
TimeProblem[🗓️ Time Problem] --> |caused by| SingleThread[🧵 Single thread]
SingleThread --> |send tasks to| ClientAPIs
TimeProblem --> |solved by| Asynchrony[🛎️ Asynchrony]
Asynchrony --> | delivered with | ClientAPIs{💻 Client APIs}
ClientAPIs --> |like| setTimeout[(⏲️ setTimeout)]
ClientAPIs --> |like| eventListener[(🦻🏾 eventListener)]
ClientAPIs --> |like| fetch[(🐕 fetch)]
```
32 changes: 6 additions & 26 deletions common-content/en/module/js3/capturing-events/index.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's bump this block's time to 20?

Original file line number Diff line number Diff line change
Expand Up @@ -27,33 +27,13 @@ function handleSearchInput(event) {
}
```

When the "input" event fires, our handler function will run. Inside the handler we can access the updated input value: `const searchTerm = event.target.value;`

So our key steps are:

1. Add an input event listener to the search box
2. In the handler, get `value` of input element
3. Set the new state based on this value.
4. Call our `render` function again.

{{<note type="warning" title="One thing at a time!">}}
But we're not going to do all of these at once! Stop and implement just the first two steps (adding the event listener, and getting the value), and `console.log` the search term.

{{</note>}}

We will make sure this works before we try to change the UI. Why? If we try to add the event listener and something _doesn't_ work, we will only have a little bit of code to debug.

If we tried to solve the whole problem (updating the UI) and something didn't work, we would have a _lot_ of code to debug, which is harder!

We've now demonstrated that we can capture search text on every keystroke:
These listeners wait for specific events to occur, and when they do, they trigger a callback function we've defined. This gives us a way to make our code respond to user actions rather than running all at once.

```js
const searchBox = document.getElementById("search");
const search = document.getElementById("search");
search.addEventListener("input", handleInput);
```

searchBox.addEventListener("input", handleSearchInput);
When we call `addEventListener`, it doesn't immediately execute the `handleInput` function. Instead, it sets up a listener that will run this function later. Event listeners are actually part of the Event API provided by web browsers - they aren't part of the core JavaScript language! When you create an event listener, you're essentially making a request to a Web API to handle this functionality for you. In this pattern, the callback function (`handleInput`) only executes when a user types. We're using functions as containers for code that needs to execute at a later time, specifically in response to user interactions. This approach lets us tell the browser exactly what actions to take once a particular event occurs.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

essentially? functionality? Can you do a Hemingway pass? 🙏


function handleSearchInput(event) {
const searchTerm = event.target.value;
console.log(searchTerm);
}
```
This pattern of using callback functions is fundamental to how we handle user interactions in web browsers. It allows our code to respond dynamically to user actions rather than executing in a simple top-to-bottom manner. The browser takes care of monitoring for events and running our callback functions at the right time, letting us focus on defining what should happen when those events occur.
31 changes: 31 additions & 0 deletions common-content/en/module/js3/re-rendering/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,37 @@ emoji= '🔁'

+++

When the "input" event fires, our handler function will run. Inside the handler we can access the updated input value: `const searchTerm = event.target.value;`

So our key steps are:

1. Add an input event listener to the search box
2. In the handler, get `value` of input element
Comment on lines +21 to +22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. Add an input event listener to the search box
2. In the handler, get `value` of input element
1. Add an input event listener to the search box.
2. In the handler, get the `value` of input element.

3. Set the new state based on this value.
4. Call our `render` function again.

{{<note type="warning" title="One thing at a time!">}}
But we're not going to do all of these at once! Stop and implement just the first two steps (adding the event listener, and getting the value), and `console.log` the search term.

{{</note>}}

We will make sure this works before we try to change the UI. Why? If we try to add the event listener and something _doesn't_ work, we will only have a little bit of code to debug.

If we tried to solve the whole problem (updating the UI) and something didn't work, we would have a _lot_ of code to debug, which is harder!

We've now demonstrated that we can capture search text on every keystroke:

```js
const searchBox = document.getElementById("search");

searchBox.addEventListener("input", handleSearchInput);

function handleSearchInput(event) {
const searchTerm = event.target.value;
console.log(searchTerm);
}
```

Now that we've shown we can log the search text, we can set the new value of the `searchTerm` state, and re-render the page.

We should have a page like this:
Expand Down
50 changes: 37 additions & 13 deletions common-content/en/module/js3/using-fetch/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,41 @@ emoji= '🌐'
render = 'never'
list = 'local'
publishResources = false

+++

So now we have these pieces of our giant concept map
So now we have these pieces of our giant concept map:

1. 📤 We know that we can send a request using `fetch()`
2. 🐕 We know that `fetch` is a 💻 client-side 🧰 Web API that requires an HTTP connection
3. 🗓️ We know that sending requests over a network takes time
4. 🧵 We know that we should not stop our program to wait for data
5. 🪃 We know that we can use Promises to manage asynchronous operations

But we still don’t know how to use `fetch` to get data from a server side API.

### Loading html files

When you double-click an HTML file in your file explorer to open it directly in your browser, you're using what's called the "file protocol" or "file scheme." In your browser's URL bar, you'll see something like:

```
file:///Users/username/projects/my-website/index.html
```

The `file://` prefix indicates that your browser is reading the file directly from your computer's filesystem, without going through a web server. While this approach might seem convenient for simple HTML files, it will prevent us from using `fetch`.

### Web Server Access: The HTTP Protocol

The second approach involves using a local development server. You can create one using tools like [Python's built-in server](https://realpython.com/python-http-server/) or [Node.js's http-server](https://www.npmjs.com/package/http-server). These tools create a web server on your computer that serves your files using the HTTP protocol. Your browser will then access the files through a URL like:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The second approach involves using a local development server. You can create one using tools like [Python's built-in server](https://realpython.com/python-http-server/) or [Node.js's http-server](https://www.npmjs.com/package/http-server). These tools create a web server on your computer that serves your files using the HTTP protocol. Your browser will then access the files through a URL like:
Another approach involves using a local development server. You can create one using tools like [Python's built-in server](https://realpython.com/python-http-server/) or [Node.js's http-server](https://www.npmjs.com/package/http-server). These tools create a web server on your computer that serves your files using the HTTP protocol. Your browser will then access the files through a URL like:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The second approach involves using a local development server. You can create one using tools like [Python's built-in server](https://realpython.com/python-http-server/) or [Node.js's http-server](https://www.npmjs.com/package/http-server). These tools create a web server on your computer that serves your files using the HTTP protocol. Your browser will then access the files through a URL like:
The second approach involves using a local development server. You can create one using tools like [Python's built-in server](https://realpython.com/python-http-server/) or [npm's http-server](https://www.npmjs.com/package/http-server). These tools create a web server on your computer that serves your files using the HTTP protocol. Your browser will then access the files through a URL like:


```
http://localhost:8000/index.html
```

1. 📤 we know that we can send a request using `fetch()`
1. 🐕 we know that `fetch` is a 💻 client side 🧰 Web API
1. 🗓️ we know that sending requests over a network takes time
1. 🧵 we know that we should not stop our program to wait for data
1. 🪃 we know that we can use callbacks to manage events
The `http://` prefix shows that you're accessing the file through a proper web server, even though that server is running on your own computer.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The `http://` prefix shows that you're accessing the file through a proper web server, even though that server is running on your own computer.
The `http://` prefix shows that you're accessing the file through a proper web server, even though that server is running on your own computer.
You need to be using `http://` (or `https://`) _not_ `file://` in order to use `fetch`.


But we still don’t know how to use `fetch` to get data from a server side API. Let’s find this out now.
## Using `fetch`

Let's pick our film display exercise back up. Before we had a list of films hard-coded in our `state`. We're going to replace the films array with data fetched from a server.
Previously, we had a list of films hard-coded in our `state`. Now, let's continue using our concept map to fetch data from a server.

```js
// Begin with an empty state
Expand All @@ -37,15 +58,18 @@ const endpoint = "https://programming.codeyourfuture.io/dummy-apis/films.json";
const fetchFilms = async () => {
const response = await fetch(endpoint);
return await response.json();
}; // Our async function returns a Promise
};

fetchFilms().then((films) => {
// When the fetchFilms Promise resolves, this callback will be called.
state.films = films;
render();
});
```

`fetch` returns a `Promise`; the `Promise` fulfils itself with a response; the response contains our data.
{{<note type="remember" title="Serving files locally">}}
Remember: If you see an error message about fetch not being allowed from `file://` URLs, that's your signal to serve your files through a local development server instead of opening them directly in the browser.
{{</note>}}

fetch returns a Promise; the Promise fulfils itself with a response; the response contains our data.

Next we will dig into `Promise`s, `async`, `await`, and `then`, and complete our concept map.
Next, we'll dig into `Promise`s, `async`, `await`, and `then` in more detail to complete our concept map.
Loading