Week 02
Asynchronous JavaScript
Asynchronous programming refers to being able to do something else while waiting for an I/O operation, such as network request, reading files, accessing GPU, etc. As JavaScript is single threaded, asynchronous programming allows us to send network requests without blocking the thread until the request returns. Otherwise, the main thread will be blocked resulting in a browser freeze.
Promises are the core part of asynchronous JavaScript. They allow us to send a network request, and instead of waiting for the response, JavaScript will give us a Promise right away, which we can later use for gathering the result.
The fetch Function
The fetch function is used to asynchronously calling an API endpoint:
// the url returns a random fox image
console.log(fetch("https://randomfox.ca/floof/")); // [object Promise] { ... }
Instead of getting the result of the API call, we received a Promise. The promise is an object that represents whether the async operation is pending, has been completed, or has failed. Basically, the browser will let us know when the result is ready.
The pending promise represents a state before the data has been fetched. We need to chain on a function called then. This function will take in a callback function that will run if the previous operation was successful. In other words, fetch some data, then do something else.
fetch("https://randomfox.ca/floof/")
.then(res => res.json())
.then(json => json.image)
.then(console.log) // https://randomfox.ca/images/73.jpg
.catch(console.error);
First, we send a GET request to receive a random fox image. Then we use then to wait for the response. Whenever the response is ready, the callback function (res => res.json()) will be called, which will convert the result to a JSON format. Whatever the function returns--in this case a JSON object--will be the input for the next then function. We gather the image field from the result and pass it to console.log. If the request fails, the function inside catch will be called. Example of a result from the endpoint:
{"image":"https:\/\/randomfox.ca\/images\/44.jpg","link":"https:\/\/randomfox.ca\/?i=44"}
Async/Await
Another way to deal with Asynchronous JavaScript is to use async/await. async refers to the function that does something asynchronously, such as sending a network request; and await is for waiting for the response to come back. Some developers prefer this approach as it reads a little bit more easily that a chain of then methods.
Let's convert the previous example to async/await:
const getFox = async () => {
let res = await fetch("https://randomfox.ca/floof/");
let json = await res.json();
let { image } = json;
console.log(image);
}
getFox(); // https://randomfox.ca/images/76.jpg
Usefule Array Methods
Map
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array. map runs a function for every element inside an array and returns a new array.
let articles = [
{
name: "Matrix 4 is Trash",
},
{
name: "Scientists Find Out Birds Can Fly",
},
];
articles = articles.map((article) => ({...article, markup: `<h1>${article.name}</h1>`}));
console.log(articles[0].name, articles[0].markup); // "Matrix 4 is Trash", "<h1>Matrix 4 is Trash</h1>"
Filter
The filter() method creates a new array with all the elements that pass the test implemented by the provided function.
const languages = [
"JavaScript",
"Python",
"Go",
"Rust",
"Java"
];
const result = languages.filter((language) => language.length < 5);
console.log(result); // ["Go","Rust","Java"]
localStorage
The Window object has a storage API we can use to store some data on the current domain (address). The data will persist until it's deleted, but will only be available on the browser and the device that were used to store the data. For instance, if you use the localStorage to persist some data on Chrome, it won't be available when you access the same address on Firefox, or on Chrome on a different device. It also won't be available in private mode. That's why ultimately, applications need to have a central backend to store the data across browsers and devices.
With JavaScript, we can store and then retrieve data to and from localStorage like this:
// store some data with the key 'name'
localStorage.setItem('name', 'alice');
// retrieve the data with the same key
const name = localStorage.getItem('name');
console.log(name); // alice
More often than not, though, you want to store data with some structure. The most common structure when we work with JavaScript is JSON. In order to store and retrieve data in JSON format, we need to use the JSON.stringify(data) and JSON.parse(data) functions. Example:
// use stringify when storing structured data
const data = {name: "Alice"};
localStorage.setItem('somekey', JSON.stringify(data));
// use parse when reading the data
const item = JSON.parse(localStorage.getItem('somekey'));
console.log(item.name); // Alice
The DOM API
The JavaScript DOM API is the way to interact with the--surprise, surprise--DOM. We use the DOM API to:
- Change the attributes of HTML elements
- Add/Remove new HTML elements
- Add/Remove events, such as mouse click or key down
- ...
Examples:
// get an element with a specific id
document.getElementById("element-id")
// get an array of elements with a specific class
document.getElementsByClassName("element-class");
// get the first element matching a specific selector
document.querySelector(".test-class");
// create new elements
document.createElement("p");
document.createElement("section");
// set attributes
const img = document.getElementById("image")
img.setAttribute('src', 'new-image-src')
// add element
const paragraph = document.createElement("p")
const content = document.createTextNode("Hello!");
paragraph.appendChild(content)
const div = document.getElementById("wrapper")
div.appendChild(paragraph)
// remove element
const element = document.getElementById("removable");
element.remove();
// add events
const button = document.getElementById("submit")
const alarm = () => alert("Clicked")
button.addEventListener("click", alarm)
// toggle a class
element.classList.toggle("class-name");
// add a class
element.classList.add("class-name");
// remove a class
element.classList.remove("class-name");