Asynchronous JavaScript
Introducing the Fetch API
Now that we know how to build our request following HTTP Protocol, it’s time to learn how to send the request.
The fetch method from the Fetch API takes in a required resource argument (a URL or Request object) and an optional options object, and returns a Promise that resolves to a Response. The syntax is simple, here’s an example of a fetch call:
const response = fetch('https://example.com/movies.json')
Data returned from an API are typically either XML (eXtensible Markup Language) or JSON (JavaScript Object Notation).
XML is similar to HTML without predefined tags. Most tags are custom to the type of data it includes.
<gif>
<type>gif</type>
<id>YsTs5ltWtEhnq</id>
<slug>confused-flying-YsTs5ltWtEhnq</slug>
<url>http://giphy.com/gifs/confused-flying-YsTs5ltWtEhnq</url>
<username>JoeCool4000</username>
</gif>
JSON is similar to JavaScript objects, using notation similar to a key/value pair.
{
"gif": {
"type": "gif",
"id": "YsTs5ltWtEhnq",
"slug": "confused-flying-YsTs5ltWtEhnq",
"url": "http://giphy.com/gifs/confused-flying-YsTs5ltWtEhnq",
"username": "JoeCool4000"
}
}
Both types of formats are widely used but JSON is more commonly used by developers. JSON is typically easier to parse and works better with JavaScript.
Since the fetch method returns a Promise that resolves to a Response object, we need to take an extra measure to extract the body and then read the data.
- On the
Responseobject, we call the.json()method, which reads the response body to completion and parses it as JSON. It returns a Promise that resolves to the resulting JavaScript value (typically an object or array). - We then access whatever properties the API has put on that parsed value (the exact shape depends on the API — there is no automatic
dataattribute).
A naive first attempt might look like this — but it won’t work as written, because fetch() returns a Promise, not a Response:
// ⚠️ Broken: fetch() returns a Promise, not a Response
const response = fetch('https://example.com/movies.json')
const jsonResponse = response.json();
const movies = jsonResponse.movies;
You may notice that when attempting to run the fetch method we provided above you will get an error in the console. This is because all of these actions execute sequentially. We don’t want to launch the next action until we know that we have successfully received the data from our first action. This is called asynchronous programming. In JavaScript we will use the keywords async and await to define an asynchronous function and await to indicate when actions should wait on the return of a promise from the previous action. We will explain this further in the next steps!
What is Asynchronous JavaScript?
Sometimes you have functions in your code that rely on information or data from another step in your program flow. You see this quite often when you are loading a web page on a browser and you see the spinning wheel :loading:.
You don’t want your users to only see a blank screen, instead, there may be other things your server can do to load other parts of your webpage while they wait for results. This is exactly the basis of asynchronous programming. Asynchronous programming relies heavily on the blocking of code to separate functions that are dependent or independent.
The fetch() method is a perfect example of an asynchronous function. In order to parse through results and call the .json() method on the response, we need to ensure that fetch was able to return a response.
Traditionally, programmers use method chaining by attaching a then() method to handle the asynchronous nature of parsing through response data. You may see older tutorials using the fetch() method follow the syntax below:
fetch('https://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
By chaining of functions, this ensures that the .json() method isn’t called unless the fetch returns a response and so on.
Introducing Promises
A Promise is a JavaScript object that represents the eventual completion (or failure) of an asynchronous operation and its resulting value. Promises are the general pattern that modern asynchronous APIs (including fetch) use to expose their results — they are not specific to HTTP requests. The Fetch API is a modern alternative to XMLHttpRequest for making HTTP requests, and it returns Promises natively, which is what allows us to use async/await with it.
The async and await syntax was introduced with the ECMAScript 2017 update. These keywords are used to indicate asynchronous functions or code.
-
asyncis used to indicate an asynchronous function and appears before the function definition.//async function async function hello() { return "Hello" }; //async function expression let hello = async function() { return "Hello" }; -
awaitis best used in combination withasyncfunction definitions. It is used with any Promise-based function to pause code or with a function that returns a Promise. Most specifically, with web API functions.async function hello() { const greeting = await Promise.resolve("Hello"); return greeting; };
Let’s take a look at how using the async and await notation can be used in place of chaining functions with the then() method.
fetch with chains of then methods
fetch('https://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
fetch with async/await
async function getMovies() {
const response = await fetch('https://example.com/movies.json');
const jsonResponse = await response.json();
console.log(jsonResponse);
}
getMovies();
await can only be used inside an async function or at the top level of an ES module — wrapping the calls in an async function lets the snippet run in either context.
This syntax may seem slightly more wordy but can come in handy as you build more complicated projects.