πŸ§‘πŸΎβ€πŸ’» prep

πŸ«±πŸΏβ€πŸ«²πŸ½ Promises

Learning Objectives

graph LR Asynchrony --> |delivered with| Promise{{🀝 Promises}} Promise --> |resolve to a| Response{πŸ“€ Response} Promise --> |join the| EventLoop{{Event Loop πŸ”}}

To get data from a server, we make a request with fetch. We act on what comes back: the response. But what happens in the middle? We already know that JavaScript is single-threaded: it can only do one thing at a time.

So do we just stop and wait? No! We have a special object to handle this time problem. Put this code in a file and run it with node:

const url = "https://api.github.com/users/SallyMcGrath"; // try your own username
const response = fetch(url);
console.log(response);
Your Promise should look like this:
Promise {
  Response {
    [Symbol(realm)]: null,
    [Symbol(state)]: {
      aborted: false,
      rangeRequested: false,
      timingAllowPassed: true,
      requestIncludesCredentials: true,
      type: 'default',
      status: 200,
      timingInfo: [Object],
      cacheState: '',
      statusText: 'OK',
      headersList: [HeadersList],
      urlList: [Array],
      body: [Object]
    },
    [Symbol(headers)]: HeadersList {
      cookies: null,
      [Symbol(headers map)]: [Map],
      [Symbol(headers map sorted)]: null
    }
  },
  [Symbol(async_id_symbol)]: 54,
  [Symbol(trigger_async_id_symbol)]: 30
}

The response variable in this code is not labelling the data. It’s labelling a Promise.

A promise is exactly what it sounds like: a promise to do something. You can use this promise object to sequence your code. You can say, “When the data comes back, then do this.”

You will explore Promises in more detail as you build more complex applications. For now, let’s move on to .then().

πŸͺ† .then()

Learning Objectives

graph LR Promise{{🀝 Promises}} --> |resolve to a| Response{πŸ“€ Response} Response ---> |sequence with| then{{πŸͺ†οΈ then}}

.then() is a method that all Promises have. You can interpret this code:

const url = "https://api.github.com/users/SallyMcGrath";
const callback = (response) => response.json(); // .json() is an instance method that exists for all Response objects.
fetch(url).then(callback);
  1. given a request to fetch some data
  2. when the response comes back / the promise resolves to a response object
  3. then do this next thing with the data / execute this callback

The .then() method takes in a callback function that will run once the promise resolves.

We can also inline the callback variable here - this code does exactly the same as the code above:

const url = "https://api.github.com/users/SallyMcGrath";
fetch(url).then((response) => response.json());

It’s a similar idea as the event loop we have already investigated, but this time we can control it clearly. The .then() method queues up callback functions to execute in sequence once the asynchronous operation completes successfully. This allows us to write code as if it was happening in time order.

πŸ’‘ tip

The then() method of a Promise always returns a new Promise.

We can chain multiple .then() calls to run more logic, passing the resolved value to the next callback in the chain. This allows us to handle the asynchronous response in distinct steps. Let’s create a getProfile function in a file, call it, and try running the file with node:

const getProfile = (url) => {
  return fetch(url)
    .then((response) => response.json()) // This callback consumes the response string and parses it as JSON into an object.
    .then((data) => data.html_url) // This callback takes the object and gets one property of it.
    .then((htmlUrl) => console.log(htmlUrl)); // This callback logs that property.
};
getProfile("https://api.github.com/users/SallyMcGrath");

So then returns a new Promise, and you can call then again on the new object. You can chain Promises in ever more complex dependent steps. This is called Promise chaining.

It’s important to understand some of what is happening with Promises and then. But for the most part, you will not be writing code in this style.

🍬 async/await

Learning Objectives

These two blocks of code do exactly the same thing:

const getProfile = async (url) => {
  const response = await fetch(url);
  const data = await response.json();
  const htmlUrl = data.html_url;
  console.log(htmlUrl);
}

getProfile("https://api.github.com/users/SallyMcGrath");
const getProfile = (url) => {
  return fetch(url)
    .then((response) => response.json())
    .then((data) => data.html_url)
    .then((htmlUrl) => console.log(htmlUrl));
};
getProfile("https://api.github.com/users/SallyMcGrath");

Async/await is syntactic sugar 🧢 🧢 syntactic sugar A simpler, or “sweeter” way to write the same thing. The code works the same under the hood, but it’s easier to read. for Promises.

We group async and await together: async/await, because we use them together. 🧢 🧢 use them together. We can only use await inside an async function or at the top level of a module.

We use the async keyword to define a function that returns a Promise. An async function always returns a Promise.

We can see this with a simple function which doesn’t need to await anything. Save this in a file and run it with node:

const getProfile = async (url) => url;

console.log(getProfile("hello")); // Logs a Promise.

getProfile("hello").then((value) => console.log(value)); // Logs a value

Even though the function above doesn’t have a time problem, the fact that we define the function as an async function means it returns a Promise.

But let’s do something more interesting - let’s actually solve a time problem.

const getProfile = async (url) => {
  // the async keyword tells us this function handles a time problem
};

We use the await operator to say “don’t move on until this is done”. Importantly, we are not actually waiting for a Promise to resolve. We are scheduling a callback that will be called when the Promise resolves. But this allows us to write code that looks like it’s happening in time order (as if we are waiting), without actually blocking our main thread.

const getProfile = async (url) => {
  const response = await fetch(url);
  return response.json();
};

getProfile("https://api.github.com/users/SallyMcGrath")
  .then((response) => console.log(response))

Save this to a file and run with with node. It works the same as before.

πŸ₯Ž try/catch

Learning Objectives

We can handle errors with a try/catch block. We can use the try keyword to try to do something, and if it fails, catch the error 🧢 🧢 error An Error is a global object produced when something goes wrong. We can throw an Error manually with the throw keyword. We can use try/catch in both synchronous and asynchronous code.

const getProfile = async (url) => {
  try {
    const response = await fetch(url);
    return response.json();
  } catch (error) {
    console.error(error);
  }
};

Let’s trigger an error to see this in action. In a Node REPL in your terminal, call getProfile on an API that does not exist again:

getProfile("invalid_url");

TypeError: Failed to parse URL from invalid_url
  [...]
  [cause]: TypeError: Invalid URL
  [...]
    code: 'ERR_INVALID_URL',
    input: 'invalid_url'

It’s actually the same error you saw before, without the word ‘Uncaught’ before it. But why do we care about this? It’s not obvious in this simple, single function. If we don’t catch the error, the function will crash. 🧢 🧢 crash. The JavaScript execution will halt with a fatal exception, causing the Node.js process to exit immediately. Any further statements will not be run.

You need to tell JavaScript what to do when something goes wrong, or it will give up completely. In fact, in synchronous programming, the entire program would crash. In asynchronous programming, only the function that threw the error will crash. The rest of the program will continue to run.

πŸ’‘ Tip

Handle your errors in all cases.

πŸ• 🎞️ fetch films

Learning Objectives

Now that we have a basic understanding of Web APIs and Promises, let’s use look again at our code for fetching film data:

const endpoint = "https://programming.codeyourfuture.io/dummy-apis/films.json";

const fetchFilms = async () => {
  const response = await fetch(endpoint);
  return await response.json();
};

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

We are defining fetchFilms: an async function - a function which returns a Promise.

When we call fetchFilms, what we get is an unresolved Promise.

What fetchFilms does is fetch a URL (with our call to fetch itself returning a Promise resolving to a Response). When the Promise from fetch resolves, fetchFilms reads the body of the Response (a string), and parses is as JSON. The Promise returned by fetchFilms then resolves with the result of parsing the string as JSON.

When the Promise from fetchFilms resolves, our next callback is called: We update our state, and call render().

After this is done, the rest of our code works exactly the same as it did before. We have our list of films in our state, so we never need to fetch the list of films again.

render works the same - it only cares that state.films is an array of films, it doesn’t care where they came from.

When we change our filter by typing, events fire and our event handler will be called back exactly the same as it did before.

Teamwork Project Presentation prep

Learning Objectives

Preparation

Watch this video about public speaking skills to improve your presentation skills.

Introduction

Being part f a product team is about teamwork and delivering working software that gives value to the user. An important aspect is selling your product, the same way you market yourself when searching for a job.

Work in a team on how all of you will present your product.

Creating the presentation

🎯 Goal: Prepare a presentation for a product pitch as a team (120 minutes)

You will create a 5-minute long presentation about:

  • Your product’s brief (what is your MVP, who are your users, what is the value, etc.)
  • How have you worked together so far
  • How have you managed conflicts within your group
  • What would your barriers be if you were to build this product in real life
  • How would you overcome those barriers to successfully launch your product

Share your presentation (e.g. PowerPoint slides, Word doc, hand-made poster, etc.). on your project board.

Practice presenting

🎯 Goal: To practice presenting your product (60 minutes)

Presentation format:

  • Each team will have 5 minutes to present
  • Everyone in your group should get a chance to speak; some of you may talk longer and some shorter

Identify which content/slides will be delivered by whom in the team.

Practice present, give each other feedback, review it and practice again:

  • is the presenter’s communication clear
  • is the presenter’s body language aligned with what they are saying
  • can the team keep time to 5 minutes (this includes the time to get the presentation up and running)