Conclusion (v1)

At this point, you should have a deep and nuanced understanding of not just how Javascript works but how you can use it with all the other knowledge you’ve gained so far to build exceptional web applications.

If you find something you don’t know or aren’t quite sure about, you should be a pro by now at Googling your way to an answer, looking at docs, and cruising through Stack Overflow posts. You’ve got all the tools you need to be a developer. And, really, the big secret is that you’ve been a web developer for a long time already.

So where do you go from here? BUILD! Build and build and build and build (see a theme in this curriculum yet?). Use what you’ve learned to create great projects. Get a job so someone else pays you to build. But focus on creating interesting software and using that to drive your education.

There’s a whole world of additional things you can learn — D3 for data visualization, other full-stack frameworks or single-page MVC front-end frameworks… all that stuff is best learned when you’ve got something you want to build that requires "the perfect tool for the job."

And speaking of jobs, if you’ve made it this far then you’ve displayed the kind of capability and drive that employers are looking for. The final step is to show that to the world and get them to pay you to keep learning.

Additional Resources

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

UPDATED: 01.02.2021

Project: Final Project (v1)

Instructions

Copy your favorite website as well as you can. Pinterest, Facebook, Twitter… Just make sure it’s got lots of interesting functionality. You’ll be integrating your full array of skills into this one….

Of course, you can’t copy every single feature and a lot of the user interface will be a bit clunkier, but you can get yourself 80% of the way there. And that’s darn impressive.

  1. Think about what you’ll need to do to get this all working together. This is where it’s really helpful to think it completely through on paper or whiteboard ahead of time! A few hours of thought here will save you from wasting days of coding. Try to lay it ALL out. An important part of this is scope — you obviously can’t build the entire website (which presumably took a full team of engineers years to produce), so you’ll need to identify the core functionality of the site and then the "nice-to-have" stuff. Make sure you will finish building the core functionality BEFORE starting to add on the rest. If you try to do it all at once, you’ll get lost and frustrated. Trust me. Everything takes longer than you expect.

  2. Build it!

  3. Try testing the very high level Javascript functionality with Jest. Don’t get too bogged down in testing, but try and save yourself time by adding high level tests so you don’t need to click around 100 times every time you make a change to something that seems important.

  4. Once you’ve finished, push to Github’s specified folder and definitely submit your project below!

Good luck!

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

UPDATED: 01.02.2021

Project: Battleship (v1)

Introduction

It’s time to really flex your muscles. Test Driven Development can certainly feel uncomfortable at first, but becomes more natural with practice. We’re going to implement the classic game ‘Battleship’. If you’ve never played it, or need a refresher you can read about it here and you can play an online version here.

Since we’re doing TDD, it’s important that you don’t get overwhelmed. Simply take it one step at a time. Write a test, then make it pass.

We have not yet discussed testing the appearance of a webpage. Doing this requires a separate set of tools, and it is outside the scope of this unit. For this assignment do your best to isolate every bit of application functionality from the actual DOM manipulation bits. You can use mocks to make sure that DOM methods like appendChild are being called, but try your best to keep those things outside of the app logic.

Assignment

  1. Begin your app by creating the Ship factory function.
    1. Your ‘ships’ will be objects that include their length, where they’ve been hit and whether or not they’ve been sunk.
    2. REMEMBER you only have to test your object’s public interface. Only methods or properties that are used outside of your ‘ship’ object need unit tests.
    3. Ships should have a hit() function that takes a number and then marks that position as ‘hit’.
    4. isSunk() should be a function that calculates it based on their length and whether all of their positions are ‘hit’.
  2. Create Gameboard factory.
    1. Note that we have not yet created any User Interface. We should know our code is coming together by running the tests. You shouldn’t be relying on console.logs or DOM methods to make sure your code is doing what you expect it to.
    2. Gameboards should be able to place ships at specific coordinates by calling the ship factory function
    3. Gameboards should have a receiveAttack function that takes a pair of coordinates, determines whether or not the attack hit a ship and then sends the ‘hit’ function to the correct ship, or records the coordinates of the missed shot.
    4. Gameboards should keep track of missed attacks so they can display them properly.
    5. Gameboards should be able to report whether or not all of their ships have been sunk.
  3. Create Player.
    1. players can take turns playing the game by attacking the enemy Gameboard.
    2. The game is played against the computer, so make ‘computer’ players capable of making random plays. The AI does not have to be smart, but it should know whether or not a given move is legal. (i.e. it shouldn’t shoot the same coordinate twice).
  4. Create the main game loop and a module for DOM interaction.
    1. At this point it is appropriate to begin crafting your User Interface.
    2. The game loop should set up a new game by creating Players and Gameboards. For now just populate each Gameboard with predetermined coordinates. You can implement a system for allowing players to place their ships later.
    3. We’ll leave the HTML implementation up to you for now, but you should display both the player’s boards and render them using information from the Gameboard class.
      1. You need methods to render the gameboards and to take user input for attacking. For attacks, let the user click on a coordinate in the enemy Gameboard.
    4. The game loop should step through the game turn by turn using only methods from other objects. If at any point you are tempted to write a new function inside the game loop, step back and figure out which class or module that function should belong to.
    5. Create conditions so that the game ends once one players ships have all been sunk. This function is appropriate for the Game module.
  5. Finish it up
    1. There are several options available for letting users place their ships. You can let them type coordinates for each ship, or investigate implementing drag and drop.
    2. You can polish the Intelligence of the computer player by having it try adjacent slots after getting a ‘hit’.
    3. Optionally, create a 2 player option that lets users take turns by passing the device back and forth. If you’re going to go this route, make sure the game is playable on a mobile screen and implement a ‘pass device’ screen so that players don’t see each others boards!

When you finish, create a new branch in the forked exercises repository and submit the Pull Request in the Quiz below.

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

More Testing (v1)

Introduction

An important basic concept in testing is isolation. You should only test one method at a time, and your tests for one function should not depend upon an external function behaving correctly – especially if that function is being tested elsewhere. The main reason for this is that when your tests fail, you want to be able to narrow down the cause of this failure as quickly as possible. If you have a test that depends on several functions, it can be hard to tell exactly what is going wrong.

Pure Functions

There are many benefits to using Test Driven Development (TDD) when you write your code. One of the biggest benefits is less obvious at first – it helps you to write better code. If you look back at some of your early projects you will probably notice how tightly coupled everything is. All of your functions include references to functions in other parts of your code, and the whole thing is filled with DOM methods or console.log().

Tightly coupled code is hard to test! Imagine trying to write tests for a function like this:

function guessingGame() {
  const magicNumber = 22;
  const guess = prompt('guess a number between 1 and 100!');
  if (guess > magicNumber) {
    alert('YOUR GUESS IS TOO BIG');
  } else if (guess < magicNumber) {
    alert('YOUR GUESS IS TOO SMALL');
  } else if (guess == magicNumber) {
    alert('YOU DID IT! 🎉');
  }
}

Making this testable requires us to split up all the different things that are happening. First, we do not need to test the functions prompt and alert because they are built in to the browser. They are external to our program and whoever wrote them has already tested them. What we do need to test is the number logic, which is much easier if we untangle it from the other functions:

function evaluateGuess(magicNumber, guess) {
  if (guess > magicNumber) {
    return 'YOUR GUESS IS TOO BIG';
  } else if (guess < magicNumber) {
    return 'YOUR GUESS IS TOO SMALL';
  } else if (guess == magicNumber) {
    return 'YOU DID IT! 🎉';
  }
}

function guessingGame() {
  const magicNumber = 22;
  const guess = prompt('guess a number between 1 and 100!');
  const message = evaluateGuess(magicNumber, guess);
  alert(message);
}

guessingGame();

In this example, the only thing we really need to test is the evaluateGuess function, which is much easier to test because it has a clear input and output and doesn’t call any external functions. This implementation is much nicer as well because it’s much easier to extend. If we wanted to switch out the prompt and alerts for methods that manipulate the DOM we can do that more simply now and if we want to make our game more advanced by letting the user make multiple guesses, that is also easier.

If we had written this program with TDD it is very likely that it would have looked more like the second example to begin with. Test driven development encourages better program architecture because it encourages you to write Pure Functions.

Mocking

There are two solutions to the ‘tightly coupled code’ problem. The first, and best option is to simply remove those dependencies from your code as we did above, but that is simply not always possible. The second option is mocking – writing "fake" versions of a function that always behaves exactly how you want. For example, if you’re testing a function that gets information from a DOM input, you really don’t want to have to set up a webpage and dynamically insert something into the input just to run your tests. With a mock function, you could just create a fake version of the input-grabbing function that always returns a specific value and use THAT in your test.

Study

  1. If you haven’t already, watch the mocking videos from this series:

  1. Too much mocking can be a bad thing. It is sometimes necessary, but if you have to set up an elaborate system of mocks to test any bit of your code, that means your code is too tightly coupled. These two articles (one and two) might be a little extreme, but they contain several really good points about program architecture and testing.

  2. Now that you have some practice and context for TDD, this section of the Jest docs will probably make good sense to you.

  3. Jest includes some really handy mocking functions. Read about them in the official docs

  4. And finally, if you wish, you can add Jest to your webpack setup. Read about that process here.

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

UPDATED: 01.02.2021

Project: Testing Practice (v1)

Introduction

Let’s practice! This testing thing really is not that difficult, but it is quite new. The only way to get comfortable with it is to spend some time doing it.

Practice

Write tests for the following functions, and then make the tests pass!

  1. capitalize(string) takes a string and returns that string with the first character capitalized.

  2. reverseString(string) takes a string and returns it reversed.

  3. A calculator object that contains the basic operations: add, subtract, divide, and multiply.

  4. Caesar Cipher. Read about it on this website and play around with this cool Caesar Cipher Exploration Tool from Khan Academy.

    1. Don’t forget to test wrapping from z to a.
    2. Don’t forget to test keeping the same case.
    3. Don’t forget to test punctuation!
    4. For this one, you may want to split the final function into a few smaller functions. One concept of Testing is that you don’t need to explicitly test every function you write… Just the public ones. So in this case you only need tests for the final caesar() function. If it works as expected you can rest assured that your smaller helper functions are doing what they’re supposed to.
  5. Array Analysis. Write a function that takes an array of numbers and returns an object with the following properties: average, min, max, and length.

    const object = analyze([1,8,3,4,2,6]);
    
    object == {
      average: 4,
      min: 1,
      max: 8,
      length: 6
    };
    

Special Note on using ES6 import statements with Jest

By default, the current version of Jest will not recognize ES6 import statements. In order for you to be able to use ES6 modules for this project you may do the following:

  1. Install the @babel/preset-env package
npm i -D @babel/preset-env
  1. Create a .babelrc file in the project’s root with the following lines of code:
{
  "presets": ["@babel/preset-env"]
 }

This will allow you to use import statements. Note that in the Jest docs a similar instruction is laid out here

Additional Resources

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

UPDATED: 20.01.2021

Testing Basics (v1)

Introduction

Test Driven Development is a big deal in the modern development landscape. This is a concept that we introduced way back in our Fundamentals section with our JavaScript Exercises. The main idea is simply that you start working on your code by writing automated tests before writing the code that is being tested. There are tons of benefits to working like this, all of which will be discussed in the resources below.

There are many test-running systems available in JavaScript: Mocha, Jasmine, Tape and Jest to name a few. Fortunately the syntax for each one is very similar. They all have their own set of special features, but the basic syntax is almost identical, so in the end it doesn’t matter which one you use. In fact, simply picking which library to use for this curriculum has been quite tricky!

This lesson is going to center around Jest. The biggest reasons for this decision are that one of the best resources we’ve found for explaining JavaScript testing uses it and they have fantastic documentation. In the end, writing tests is less about the syntax and more about the TDD philosophy. The most important issues are knowing why we write tests and what we test rather than how.

Study

  1. Read this short article that outlines the basic process and the benefits of TDD.

  2. Watch at least the first 3 videos of this video series about testing in JavaScript. The first video focuses heavily on the WHY, while the next two go into more depth about the process. Later videos in the series are definitely worthwhile, but the first 3 are enough to get you up and running.

  3. Create a new folder, be careful the name you give it should not contain any spaces (e.g no: "Jest App", yes: "JestApp"). Open Visual Studio Code and navigate to that folder. Open the terminal and type npm init --yes to initialize a default package.json file. Then read and follow the Getting Started tutorial on the main Jest website.

  4. Read and follow the Using Matchers document on the main Jest website. This one demonstrates some of the other useful functions you can use in your tests.

  5. Watch this amazing next video that covers what to test in your codebase. The video is specifically about testing the Ruby language, but that doesn’t matter at all. The concepts here ring true in any language, and luckily Ruby is a clear enough language that you will be able to follow along just fine. This video might be worth re-visiting after you’ve done some testing of your own.

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

UPDATED: 08.03.2021

Project: Weather App (v1)

Introduction

Use everything we’ve been discussing to create a weather forecast site using the weather API from the previous lesson. You should be able to search for a specific location and toggle displaying the data in Fahrenheit or Celsius.

You should change the look of the page based on the data, maybe by changing the color of the background or by adding images that describe the weather. (You could even use the Giphy API to find appropriate weather-related gifs and display them). Feel free to use promises or async/await in your code, though you should try to become comfortable with both.

  1. Set up a blank HTML document with the appropriate links to your JavaScript and CSS files.

  2. Write the functions that hit the API. You’re going to want functions that can take a location and return the weather data for that location. For now, just console.log() the information.

  3. Write the functions that process the JSON data you’re getting from the API and return an object with only the data you require for your app.

  4. Set up a simple form that will let users input their location and will fetch the weather info (still just console.log() it).

  5. Display the information on your webpage!

  6. Add any styling you like!

  7. Optional: add a ‘loading’ component that displays from the time the form is submitted until the information comes back from the API.

  8. Push that baby to github and share your solution below!

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

Async and Await (v1)

Introduction

Asynchronous code can become difficult to follow when it has a lot of things going on. async and await are two keywords that can help make asynchronous read more like synchronous code. This can help code look cleaner while keeping the benefits of asynchronous code.

For example, the two code blocks below do the exact same thing, they both get information from a server, process it, and return a promise.

function getPersonsInfo(name) {
  return server.getPeople().then(people => {
    return people.find(person => { return person.name === name });
  });
}
async function getPersonsInfo(name) {
  const people = await server.getPeople();
  const person = people.find(person => { return person.name === name });
  return person;
}

The second example looks much more like the kind of functions you are used to writing, however, did you notice the async keyword before the function declaration? How about the await keyword before server.getPeople()?

Learning Objectives

  1. How do you declare an async function?
  2. What does the async keyword do?
  3. What does the await keyword do?
  4. What is returned from an async function?
  5. What happens when an error is thrown inside an async function?
  6. How can you handle errors inside an async function?

The async keyword

The async keyword is what lets the javascript engine know that you are declaring an asynchronous function, this is required to use await inside any function. When a function is declared with async, it automatically returns a promise, returning in an async function is the same as resolving a promise, likewise, throwing an error will reject the promise.

An important thing to understand is async functions are just syntactical sugar for promises.

The async keyword can also be used with any of the ways a function can be created, said differently: it is valid to use an async function anywhere you can use a normal function. Below you will see some examples that may not be intuitive, if you don’t understand them, come back and take a look when you are done with the assignments.

  const yourAsyncFunction = async () => {
    // do something asynchronously and return a promise
    return result;
  }
 anArray.forEach(async item => {
   // do something asynchronously for each item in 'anArray'
   // one could also use .map here to return an array of promises to use with 'Promise.all()'
 });
server.getPeople().then(async people => {
  people.forEach(person => {
    // do something asynchronously for each person
  });
});

The await keyword

await is pretty simple: it tells javascript to wait for an asynchronous action to finish before continuing the function. It’s like a ‘pause until done’ keyword. The await keyword is used to get a value from a function where you would normally use .then(). Instead of calling .then() after the asynchronous function, you would simply assign a variable to the result using await, then you can use the result in your code as you would in your synchronous code.

Error Handling

Handling errors in async functions is very easy. Promises have the .catch() method for handling rejected promises, and since async functions just return a promise, you can simply call the function, and append a .catch() method to the end.

asyncFunctionCall().catch(err => {
  console.error(err)
});

But there is another way: the mighty try/catch block! If you want to handle the error directly inside the async function, you can use try/catch just like you would inside synchronous code.

async function getPersonsInfo(name) {
  try {
    const people = await server.getPeople();
    const person = people.find(person => { return person.name === name });
    return person;
  } catch (error) {
    // Handle the error any way you'd like
  }
}

Doing this can look messy, but it is a very easy way to handle errors without appending .catch() after your function calls. How you handle the errors is up to you, and which method you use should be determined by how your code was written. You will get a feel for what needs to be done over time. The assignments will also help you understand how to handle your errors.

Practice

Remember the Giphy API practice project? (If not, you should go back and complete the API lesson) We are going to convert the promise based code into async/await compatible code. Here’s a refresher of the code we are starting with:

<script>
  const img = document.querySelector('img');
  fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'})
    .then(function(response) {
      return response.json();
    })
    .then(function(response) {
      img.src = response.data.images.original.url;
    });
</script>

Since await does not work on the global scope, we will have to create an async function that wraps our API call to Giphy.

<script>
  const img = document.querySelector('img');

  async function getCats() {
    fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'})
      .then(function(response) {
        return response.json();
      })
      .then(function(response) {
        img.src = response.data.images.original.url;
      })
  }
</script>

Now that we have a function that is asynchronous, we can then start refactoring from using promises to using await:

<script>
  const img = document.querySelector('img');

  async function getCats() {
    const response = await fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'});
    response.json().then(function(response) {
      img.src = response.data.images.original.url;
    });
  }
</script>

Since response is still the same object we have passed to the .then() block at the start, we still need to use the .json() method, which in turn returns a promise. Because .json() returns a promise, we can use await to assign the response to a variable.

<script>
  const img = document.querySelector('img');

  async function getCats() {
    const response = await fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'});
    const catData = await response.json();
    img.src = catData.data.images.original.url;
  }
</script>

To use this function, we just simply need to call it with getCats() in our code.

<script>
  const img = document.querySelector('img');

  async function getCats() {
    const response = await fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'});
    const catData = await response.json();
    img.src = catData.data.images.original.url;
  }
  getCats();
</script>

This code will behave exactly like the code from the last lesson, it just looks a bit different after refactoring. async/await are very useful tools when it comes to cleaning up asynchronous javascript code. It is important to remember async/await are just promises written in a different way. Do the assignments below, and dive deeper into the understanding of async/await.

  1. Read this article for a solid introduction to async/await. this article also has some good examples of it’s use.

  2. Watch the next video for a good overview on async/await and it’s purpose, along with a special trick.

  1. If you feel brave enough, read this article for a more in-depth look at async/await, including how to handle errors. (Student note: might be far too advanced for now, since it mentions documents databases and several other backend stuff!)

Advanced Async/Await

When using the async/await pair in a loop, and especially in a loop callback function such as in foreach or `map, things start to get more complicated. Take a long break and read this interesting and important article on JavaScript async and await in loops.

Additional Resources

  1. This video is an example of how you can change callbacks, to promises, to async/await.

  1. This video gives a comprehensive view of Promises, async, and await.

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

UPDATED: 22.05.2021

  • [10.03.2021] Added article on async/await in loops

CONTRIBUTORS:

Working with APIs (v1)

Introduction

One of the most powerful things a web developer can do is fetching data from a server and displaying it creatively on their site. In many cases, the server solely exists for that specific site. The server could contain blog posts, user data, high scores for a game or anything else. In other cases, the server is an open service that serves data to anyone that wants to use it (i.e. weather data or stock prices). In either case, the methods of accessing and then using that data are essentially the same.

APIs

Servers that are created for serving data for external use (in websites or apps) are often referred to as APIs or Application Programming Interfaces.

There are multiple ways of requesting data from an API, but all of them basically do the same thing. For the most part, APIs are accessed through URLs, and the specifics of how to query these URLs changes based on the specific service you are using. For example, the OpenWeatherMap API has several types of data that you can request. To get the current weather in a specific location, you need to request data from this URL:

api.openweathermap.org/data/2.5/weather?q=London,uk

You’ll want to switch out the city for the location you’re requesting. The specifics for using any API are usually documented on the service’s website. Check here for the OpenWeatherMap API documentation.

If you haven’t already, go ahead and paste the weather URL above into your browser…(we’ll wait).

Unless the implementation of that specific API has changed, you probably get an error like this:

{"code":401, "message": "Invalid API key. Please see http://openweathermap.org/faq#error401 for more info."}

This brings us to another point about APIs. In most cases, you have to sign up and get an API key to use them. Obtaining the API key is as simple as signing up on their website and using it is usually as easy as pasting it into the URL:

http://api.openweathermap.org/data/2.5/weather?q=London,uk&APPID=1111111111

(exactly how to include the key changes from service to service)

Services like the OpenWeatherMap use API keys to track who is requesting the data they serve, and how much data they are requesting. The reason they do this is so that people can’t take advantage of their service. Running servers, especially large ones costs money, and while each current weather request (or whatever) is relatively cheap, if the amount of requests gets too high the cost could be significant. Imagine using that API to create an amazing weather app that gets used all over the world….you could easily have thousands of people accessing that data every minute!

By signing up for a service and getting an API key you are letting the service track how much you are actually using. In many cases services are limited as to how much data they can request for free. With the weather app example, their free plan only allows you to make 60 requests per minute and also limits what types of data you can access (details here if you’re interested). So, if your app became successful, you would probably need to pay for a better account.

Luckily for us, the majority our apps are only going to be used by us and the people that view our portfolios. So we’ll get by just fine with free services.

Once you get a key (try this now if you like!) and waited for its activation (see Do I need to activate my API key?) you can paste the URL into the browser again (including your key of course) and hopefully, you’ll see a proper response:

{
    "coord": {
        "lon": -77.73,
        "lat": 38.77
    },
    "weather": [ {
        "id": 800,
        "main": "Clear",
        "description": "clear sky",
        "icon": "01d"
    } ],
    "base": "stations",
    "main": {
        "temp": 75.74,
        "pressure": 1017,
        "humidity": 57,
        "temp_min": 71.6,
        "temp_max": 78.8
    },
    "visibility": 16093,
    "wind": {
        "speed": 3.87,
        "deg": 291
    },
    "clouds": {
        "all": 1
    },
    "dt": 1504188900,
    "sys": {
        "type": 1,
        "id": 2886,
        "message": 0.0053,
        "country": "US",
        "sunrise": 1504175992,
        "sunset": 1504222878
    },
    "id": 4775660,
    "name": "New Baltimore",
    "cod": 200
}

Fetching Data

So how do we actually get the data from an API into our code?

A couple of years ago the main way to access API data in your code was using an XMLHttpRequest. This function still works in all browsers, but unfortunately, it is not particularly nice to use. The syntax looks something like this:

// Just getting XHR is a mess!
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
  request = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
  try {
    request = new ActiveXObject('Msxml2.XMLHTTP');
  }
  catch (e) {
    try {
      request = new ActiveXObject('Microsoft.XMLHTTP');
    }
    catch (e) {}
  }
}

// Open, send.
request.open('GET', 'https://url.com/some/url', true);
request.send(null);

Ouch. That was painful.

Developers, feeling the pain of having to write that stuff out, began writing 3rd party libraries to take care of this and make it much easier to use. Some of the more popular libraries are axios and superagent, both of which have their strengths and weaknesses.

More recently, however, web browsers have begun to implement a new native function for making HTTP requests, and that’s the one we’re going to use and stick with for now. Meet fetch:

// URL (required), options (optional)
fetch('https://url.com/some/url')
  .then(function(response) { 
    // Successful response :)
  })
  .catch(function(err) {
    // Error :(
  });

In case you’ve forgotten, scroll back up and look at how you would use XHR to do the same thing. While you’re admiring how nice and clean that code is, notice the .then() and .catch() functions there. Do you remember what those are? (PROMISES!)

Let’s change up our API for this example. We’re going to walk through an example using fetch with the giphy API to display a random gif on a webpage. The API requires you to sign up and get a free API key, so go ahead and do that here.

Giphy has several methods for searching and finding gifs which you can read about in their documentation. Today we’re just going to use the ‘translate’ endpoint because it’s the simplest one for our purposes. You can find the appropriate URL in their documentation by scrolling down here. What it tells us is that the correct URL is api.giphy.com/v1/gifs/translate and that it requires 2 parameters, your api_key and a search term. If you put it all together correctly (with YOUR API key) you should get something like this:

'https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats'
// of course we're searching for cats

Go ahead and try that URL (with YOUR API key) in a browser. If everything goes well you should get a relatively long string of data and no errors.

CORS

A side note before we start putting this into our code. For security reasons, by default, browsers restrict HTTP requests to outside sources (which is exactly what we’re trying to do here). There’s a very small amount of setup that we need to do to make fetching work. Learning about this is outside our scope right now, but if you want to learn a bit about it this wikipedia article is a decent starting point.

Whether or not you took the detour to learn all about Cross Origin Resource Sharing (CORS) the fix is simple. With fetch, you are able to easily supply a JavaScript object for options. It comes right after the URL as a second parameter to the fetch function:

fetch('url.url.com/api', {
  mode: 'cors'
});

Simply adding the {mode: 'cors'} after the URL, as shown above, will solve our problems for now. In the future, however, you may want to look further into the implications of this restriction.

Let’s Do This

For now, we’re going to keep all of this in a single HTML file. So go ahead and create one with a single blank image tag and an empty script tag in the body.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <img src="#" />
  <script>
  </script>
</body>
</html>

In the script tag, let’s start by selecting the image and assigning it to a variable so that we can change the URL once we’ve received it from the Giphy API.

<script>
  const img = document.querySelector('img');
</script>

Adding fetch with our URL from above is also relatively easy:

<script>
  const img = document.querySelector('img');
  fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'})
    .then(function(response) {
      console.log(response.json());
    });
</script>

You should now be able to open the HTML file in your browser, and while you won’t see anything on the page, you should have something logged in the console. The trickiest part of this whole process is deciphering how to get to the data you desire from the server’s response. In this case, inspecting the browser’s console will reveal that what’s being returned is another Promise… to get the data we need another .then() function.

<script>
  const img = document.querySelector('img');
  fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'})
    .then(function(response) {
      return response.json();
    })
    .then(function(response) {
      console.log(response);
    });
</script>

Now we have a JavaScript object and if you inspect it closely enough you’ll find that the data we need (an image URL) is nested rather deeply inside the object:

response

To get to the data we need to drill down through the layers of the object until we find what we want!

<script>
  const img = document.querySelector('img');
  fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'})
    .then(function(response) {
      return response.json();
    })
    .then(function(response) {
      console.log(response.data.images.original.url);
    });
</script>

Running the file should now log the URL of the image. All that’s left to do is set the source of the image that’s on the page to the URL we’ve just accessed:

<script>
  const img = document.querySelector('img');
  fetch('https://api.giphy.com/v1/gifs/translate?api_key=YOUR_KEY_HERE&s=cats', {mode: 'cors'})
    .then(function(response) {
      return response.json();
    })
    .then(function(response) {
      img.src = response.data.images.original.url;
    });
</script>

If all goes well, you should see a new image on the page every time you refresh!

If you’ve gotten lost along the way, check the code below. Besides the glorious styling, this is what your version should look like.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <style>
    body {
      background-color: #e6d8e7;
      display: flex;
      height: 90vh;
      align-items: center;
      justify-content: center;
    }
  </style>
  <img src="#" alt="cat">
  <script>
  const img = document.querySelector('img')
  fetch('https://api.giphy.com/v1/gifs/translate?api_key=bb2006d9d3454578be1a99cfad65913d&s=cat', {mode: 'cors'})
    .then(function(response) {
      return response.json()
    })
    .then(function(response) {
      img.src = response.data.images.original.url
    })
    .catch(e => {
      console.log(e)
    })
</script>
</body>
</html>

Study

  1. Read the fetch documentation here. It’s not all that complicated to use, but we’ve only really scratched the surface at this point. You can also watch the last part of this live session which shows XHR and the Fetch API getting data from the Rest Countries JSON API.

  2. Check out this list of free, open APIs and let your imagination go wild.

  3. Expand on our little project here by adding a button that fetches a new image without refreshing the page.

  4. Add a search box so users can search for specific gifs. You should also investigate adding a .catch() to the end of the promise chain in case Giphy doesn’t find any gifs with the searched keyword. Add a default image, or an error message if the search fails.

CONTRIBUTORS

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

UPDATED: 02.05.2021

Async (v1)

Introduction

Since JavaScript is the language of the web, there are some functions that by necessity are going to take a decent amount of time to complete, such as fetching data from a server to display on your site. For this reason, JavaScript includes support for asynchronous functions, or to put it another way, functions that can happen in the background while the rest of your code executes.

Learning Objectives

  1. What is a callback?
  2. What’s a promise?
  3. What are circumstances when promises are better than callbacks?
  4. What does the .then() function do?

Callbacks

In the recent past, the way that these were most commonly handled were with callbacks, and even now they are still used quite a lot in certain circumstances.

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action. MDN

Callbacks are simply functions that get passed into other functions. For example:

myDiv.addEventListener("click", function(){
  // do something!
})

Here, the function addEventListener() takes a callback (the "do something" function) and then calls it when myDiv gets clicked.

You will likely recognize this pattern as something that happens all the time in JavaScript code. Unfortunately, though they are useful in situations like the above example, using callbacks can get out of hand, especially when you need to chain several of them together in a specific order. The rest of this lesson discusses patterns and functions that will help keep you out of Callback hell.

Take a moment to skim through this article before moving on. Or, if you prefer a video watch this.

Promises

There are multiple ways that you can handle asynchronous code in JavaScript, and they all have their use cases. Promises are one such mechanism, and they’re one you will see somewhat often when using other libraries or frameworks. Knowing what they are and how to use them is quite useful.

Essentially, a promise is an object that might produce a value at some point in the future. Here’s an example:

Lets say getData() is a function that fetches some data from a server and returns it as an object that we can use in our code:

const getData = function() {
  // go fetch data from some API...
  // clean it up a bit and return it as an object:
  return data
}

The issue with this example is that it takes some time to fetch the data, but unless we tell our code that, it assumes that everything in the function happens essentially instantly. So, if we try to do this:

const myData = getData()
const pieceOfData = myData['whatever']

We’re going to run into trouble because when we try to extract pieceOfData out of the returned data, the function getData() will most likely still be fetching, so myData will not be the expected data, but will be undefined. Sad.

We need some way to solve this problem, and tell our code to wait until the data is done fetching to continue. Promises solve this issue. We’ll leave learning the specific syntax for the articles you’re about to read, but essentially Promises allow you to do this:

const myData = getData() // if this is refactored to return a Promise...

myData.then(function(data){ // .then() tells it to wait until the promise is resolved
  const pieceOfData = data['whatever'] // and THEN run the function inside
})

Of course there many more occasions where one would want to use Promises beyond fetching data, so learning these things now will be very useful to you.

Study

  1. Read this article. It’s a good starting place and it’s short and to the point.

  2. Watch the next video. It’s a good place to get a feel for how one might actually use promises in the wild. Feel free to watch the other videos in the series, but they aren’t strictly needed at this point. The video also mentions the ES5/ES6 issue, don’t worry about that at this point either. All major browsers support Promises and we will teach you how to support older browsers in a later lesson.

  1. Watch this video to understand how asynchronous code works in JavaScript.

  1. Read Chapter 2: Callbacks and Chapter 3: Promises from You Don't Know JS. In Chapter 2, the author explains the problems with callbacks and why callback hell will be your worst enemy (hint: it’s the inversion of control and non-linear nature of callbacks). In Chapter 3, you go deep into the how and why of promises. This chapter is not the easiest read, but you’ll be a promise professional if you take the time to properly digest it. It’s worth the effort.

Additional Resources

  1. This is another useful article about Callback functions in JavaScript.

  2. The MDN Documentation for Promises. It might not be the best resource for learning all about them, but once you’ve read a more friendly article or tutorial, this will probably be the place you return to for a refresher.

  3. This video and this one too are both nice introductions to Promises if you need more repetition.

  4. This tutorial is another good introduction.

Feedback

  • Is there anything we can help with up to this point? Do you have something to suggest about this chapter? Let us know in the comments below.

UPDATED: 13.01.2021