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

Merged
merged 15 commits into from
Jan 28, 2025
Merged
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
84 changes: 0 additions & 84 deletions common-content/en/module/js3/asynchrony/index.md

This file was deleted.

36 changes: 7 additions & 29 deletions common-content/en/module/js3/capturing-events/index.md
Dedekind561 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
+++
title = 'Capturing the user event'

time = 15
time = 20
emoji= '🦻🏻'
[objectives]
1='Add an event listener to a user input'
[build]
render = 'never'
list = 'local'
publishResources = false

+++

We've introduced our state, and our render works for different values of that state. But users of our website can't change the `searchTerm` state themselves. We need to introduce a way for them to change the `searchTerm` state via the UI.
Expand All @@ -26,33 +24,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 part of the web browser's Event API. But event listeners themselves aren't part of the core JavaScript language! When you create an event listener, you're 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. These callback functions need to execute in response to user interactions. This lets us tell the browser exactly what actions to take once a particular event occurs.

function handleSearchInput(event) {
const searchTerm = event.target.value;
console.log(searchTerm);
}
```
Callback functions are essential for handling user interactions in web browsers. They allow our code to execute in response to an event. The browser listens for events and executes our callback functions at the right time. It is our job to define 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 @@ -13,6 +13,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 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
43 changes: 43 additions & 0 deletions common-content/en/module/js3/synchronous-execution/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
+++
title = 'Synchronous execution'

time = 10
emoji= '⏳'
[objectives]
1="Define asynchrony"
2="Explain how synchronous execution works"
[build]
render = 'never'
list = 'local'
publishResources = false
+++

We can handle latency using {{<tooltip title="asynchronous execution">}}Asynchronous execution is running code in a different order than it was written.{{</tooltip>}} To understand asynchrony we first need to be clear about {{<tooltip title="synchronous execution">}}Synchronous execution is running code in the order it is written.{{</tooltip>}}.

We have written a lot of JavaScript programs that execute sequentially. This means that each line of code is run in order, one after the other.
{{<columns>}}

#### For example:

```js
console.log("first");
console.log("second");
console.log("third");
```

<--->

#### Outputs:

```console
first
second
third
```

{{</columns>}}
Each line of code is run in order. This is synchronous execution. We do this because JavaScript is {{<tooltip title="single threaded">}}
A single thread can do one thing at a time. JavaScript is a single threaded language.
{{</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**.
52 changes: 39 additions & 13 deletions common-content/en/module/js3/using-fetch/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,43 @@ 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.

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
### Loading html files

But we still don’t know how to use `fetch` to get data from a server side API. Let’s find this out now.
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:

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.
```
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

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 [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
```

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.
Dedekind561 marked this conversation as resolved.
Show resolved Hide resolved

You need to be using `http://` (or `https://`) _not_ `file://` in order to use `fetch`.

## Using `fetch`

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 @@ -36,15 +59,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.
4 changes: 2 additions & 2 deletions org-cyf-itp/content/data-flows/sprints/3/prep/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ src="https://www.youtube.com/watch?v=J-0XkB54yp8"
name="Latency"
src="module/js3/latency"
[[blocks]]
name="Asynchrony"
src="module/js3/asynchrony"
name="Synchronous execution"
src="module/js3/synchronous-execution"
[[blocks]]
name="Callbacks"
src="module/js3/callbacks"
Expand Down
Loading