From 1f825e056d695432ee76dda727027fe0af23bee8 Mon Sep 17 00:00:00 2001 From: Dedekind561 Date: Tue, 14 Jan 2025 08:57:14 +0000 Subject: [PATCH 1/4] move more details on events from asychrony to capturing-events --- .../en/module/js3/asynchrony/index.md | 39 ------------------- .../en/module/js3/capturing-events/index.md | 32 +++------------ 2 files changed, 6 insertions(+), 65 deletions(-) diff --git a/common-content/en/module/js3/asynchrony/index.md b/common-content/en/module/js3/asynchrony/index.md index 0c53a1e8b..9f8f4b24b 100644 --- a/common-content/en/module/js3/asynchrony/index.md +++ b/common-content/en/module/js3/asynchrony/index.md @@ -44,42 +44,3 @@ A single thread can do one thing at a time. JavaScript is a single threaded lang {{}}. 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 - -```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)] -``` diff --git a/common-content/en/module/js3/capturing-events/index.md b/common-content/en/module/js3/capturing-events/index.md index 0375545d5..f58839af0 100644 --- a/common-content/en/module/js3/capturing-events/index.md +++ b/common-content/en/module/js3/capturing-events/index.md @@ -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. - -{{}} -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. - -{{}} - -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. -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. From 46742d47fca9610355d1d549b0748f2d690f6588 Mon Sep 17 00:00:00 2001 From: Dedekind561 Date: Tue, 14 Jan 2025 08:57:42 +0000 Subject: [PATCH 2/4] shuffle explanation into re-rendering --- .../en/module/js3/re-rendering/index.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/common-content/en/module/js3/re-rendering/index.md b/common-content/en/module/js3/re-rendering/index.md index 45f4b5fcd..b16b43191 100644 --- a/common-content/en/module/js3/re-rendering/index.md +++ b/common-content/en/module/js3/re-rendering/index.md @@ -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 +3. Set the new state based on this value. +4. Call our `render` function again. + +{{}} +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. + +{{}} + +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: From 766ae0fe249918f1f9805298b02104f5dc4a3721 Mon Sep 17 00:00:00 2001 From: Dedekind561 Date: Tue, 14 Jan 2025 08:58:11 +0000 Subject: [PATCH 3/4] add first pass of file protocol explanation into using-fetch --- .../en/module/js3/using-fetch/index.md | 56 +++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/common-content/en/module/js3/using-fetch/index.md b/common-content/en/module/js3/using-fetch/index.md index cdb09af3e..eca899481 100644 --- a/common-content/en/module/js3/using-fetch/index.md +++ b/common-content/en/module/js3/using-fetch/index.md @@ -13,17 +13,40 @@ emoji= '🌐' +++ -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()` -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 +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. Let’s find this out now. +But we still don’t know how to use `fetch` to get data from a server side API. -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. +## 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: + +``` +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. + +Now, let's continue with our concept map: +Let's apply this knowledge to our film display exercise. Previously, we had a list of films hard-coded in our `state`. Now we'll fetch that data from a server: ```js // Begin with an empty state @@ -32,20 +55,31 @@ const state = { searchTerm: "", }; +// The endpoint we're fetching data from const endpoint = "https://programming.codeyourfuture.io/dummy-apis/films.json"; +// This async function handles fetching our film data const fetchFilms = async () => { + // Make sure you're running this through a local server (http://) + // not directly from a file (file://) 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. + // 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. +Here's what's happening: + +1. `fetch` returns a `Promise` +2. The `Promise` fulfills itself with a response from the server +3. We process that response into JSON format +4. Finally, we update our application state with the retrieved data + +Next, we'll dig into `Promise`s, `async`, `await`, and `then` in more detail to complete our concept map. -Next we will dig into `Promise`s, `async`, `await`, and `then`, and complete our concept map. +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. From d15b1373e60643959c2612d9ed795bb696042d28 Mon Sep 17 00:00:00 2001 From: Dedekind561 Date: Tue, 14 Jan 2025 09:11:34 +0000 Subject: [PATCH 4/4] tidy the explanation --- .../en/module/js3/using-fetch/index.md | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/common-content/en/module/js3/using-fetch/index.md b/common-content/en/module/js3/using-fetch/index.md index eca899481..524df1782 100644 --- a/common-content/en/module/js3/using-fetch/index.md +++ b/common-content/en/module/js3/using-fetch/index.md @@ -10,7 +10,6 @@ emoji= '🌐' render = 'never' list = 'local' publishResources = false - +++ So now we have these pieces of our giant concept map: @@ -23,7 +22,7 @@ So now we have these pieces of our giant concept map: But we still don’t know how to use `fetch` to get data from a server side API. -## Loading html files +### 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: @@ -33,11 +32,9 @@ 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): +### Web Server Access: The HTTP Protocol -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 [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: ``` http://localhost:8000/index.html @@ -45,8 +42,9 @@ 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. -Now, let's continue with our concept map: -Let's apply this knowledge to our film display exercise. Previously, we had a list of films hard-coded in our `state`. Now we'll fetch that data from a server: +## 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 @@ -55,31 +53,23 @@ const state = { searchTerm: "", }; -// The endpoint we're fetching data from const endpoint = "https://programming.codeyourfuture.io/dummy-apis/films.json"; -// This async function handles fetching our film data const fetchFilms = async () => { - // Make sure you're running this through a local server (http://) - // not directly from a file (file://) 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(); }); ``` -Here's what's happening: +{{}} +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. +{{}} -1. `fetch` returns a `Promise` -2. The `Promise` fulfills itself with a response from the server -3. We process that response into JSON format -4. Finally, we update our application state with the retrieved data +fetch returns a Promise; the Promise fulfils itself with a response; the response contains our data. Next, we'll dig into `Promise`s, `async`, `await`, and `then` in more detail to complete our concept map. - -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.