Asynchronous JavaScript Requests

Notes by James Priest

Asynchronous JavaScript Requests

<– back to Mobile Web Specialist Nanodegree homepage


Course

APIs

Docs

Lesson 1. Ajax with XHR

1.1 Course Intro

Welcome to this course on Ajax.

The main concept of Ajax is simple. You make a request for some data and then without pausing everything to wait for the requests to return, you just move on and do something else.

ajax1-1

Then once the request finally does get returned, you deal with it. Really that’s Ajax in a nutshell.

Now Ajax used to be an acronym for asynchronous JavaScript and XML and you’ll still kind of see it that way but it’s a bit of a misnomer now. Instead, Ajax is the concept of asynchronously requesting data whether that’s an XML file a JavaScript file or JSON from a REST API.

It really doesn’t matter what the data is. You just request the data asynchronously and then deal with it when it comes back.

In this course you’ll learn to make Ajax requests using JavaScript. First, we’ll look at doing this with the tried and true xhr object. Then we’ll look at how jQuery makes Ajax requests in lesson two. Finally in lesson three we’ll check out the new and massively improved way of making async requests using the fetch API.

You’re gonna love it.

So my mom’s birthday is coming up soon and I need to make a cake for her. So, while I go to the store to get the ingredients, some of my friends are going to help demo how Ajax communication works when interacting with a server.

1.2 Client Server Demo

Let’s start with what a request is. It’s actually pretty useful to imagine the internet as a bunch of people who are just simply sending messages back and forth.

ajax1-2

For this example,

  1. I’ll be the client, which means that I’m actually a browser like Chrome or Firefox.
  2. Jessica represents the internet. She is the middleman and will be passing information back and forth.
  3. Ben here is the server, which means that he’s computer dedicated to providing content to browsers like me.

If I want something from Ben, I’ll send a GET Request to him. A GET Request is a message that tells him who I am and what I want.

So, got my message, Jessica takes it and passes it to Ben. Ben, the server, looks at my message and then sends back what I requested. This is called a Response. I can open the response and then do something with it.

In order for a website to open, it performs many requests for data, like Ben and I are doing right now. Most of the time, the response is critical for the page to load. For instance, I must have Ben’s HTML to load the page and the CSS to lay it out.

When your browser makes a request synchronously, or without AJAX, it has to wait for responses before proceeding with the load.

AJAX is special because it allows these types of requests asynchronously, which means that they can happen in the background without blocking the rest of the page load.

Let’s take this analogy just a little bit further. Let’s imagine I send off a get request to Ben, but I’ve got some plans in mind for what I want to do with it when the response gets back.

So, when I send off the request, I will take the instructions and set them aside for myself. And they’ll patiently wait right here. And now, I’m free to go off and work on other tasks.

When Ben’s response eventually gets back, I’ll open up the response, take a look at my instructions, and then do something with them.

These instructions that I set aside are called a callback, in that I call them when I get a response back.

Vocabulary

1.3 Ajax Definitions & Examples

ajax1-3

Ajax requests allow for content retrieval and display without reloading the webpage.

Asynchronous in Ajax refers to the fact that the request doesn’t block other events from happening. Instead the page keeps on doing its thing and then only acts on the data when it gets for his turn by the server.

Ajax requests occur in a number of different ways and with varying levels of difficulty. Some require an API key, others use OAuth, and some don’t use any authentication at all.

And the data returned by different Ajax requests differs too.

ajax1-4

The X and Ajax stands for XML which used to be the dominant hierarchical data format but today JSON is much more popular. In fact, most Ajax requests nowadays are actually AJAJ requests - standing for asynchronous JavaScript and JSON requests, but it doesn’t sound as nice so we still just call them Ajax.

Embedded within Ajax responses it’s pretty common to see HTML which websites can use to fill in part of the page.

History lesson

JavaScript frameworks and Single Page Apps are the way to build today, but let’s review where we’ve come from.

In the traditional server-rendered web application, the client computer makes a request for a web page. The server creates and returns a page to the client. Finally, the client loads the new page and displays the information. If they interact with the page, say to add or remove something by submitting a form, they start the cycle all over again. The client will make another request, the server returns a totally new page, the client loads and presents it to the user again.

Up until the mid 2000s, this was basically the only way internet communication occurred. Information would reside on the server, and a client would request that data and refresh the page and display it. This cycle would repeat for each and every new page request.

In the late 90s, the Microsoft Outlook team added the XMLHTTP component to Internet Explorer and built a web version of the Outlook mail client. This code was later picked up by other browsers as XMLHttpRequest. This allowed browsers to make HTTP requests from Javascript and update the current page in place without fetching an entire page from the server. Instead of the synchronous model of waiting for a whole page, the user interface could update asynchronously as the user kept working. Most of the data being exchanged used the XML format.

AJAX In 2005, Jesse James Garrett coined the term AJAX to mean “Asynchronous Javascript and XML”. This is essentially the technique of using XMLHTTPRequest to fetch data and then modify the current page.

AJAX took the web world by storm, spreading far beyond Microsoft Outlook. State-of-the-art web applications like Flickr, GMail, and Google Maps rapidly adopted it. Instead of having to wait for data and have the entire page refresh, these new, near instantaneous applications were incredible.

Browser Inconsistencies Hold up, though. Ajax wasn’t all ponies and rainbows. There were several different, incompatible browser implementations and developers were forced to code for one browser or write complex code for them all. Eventually, JavaScript libraries like jQuery and YUI emerged to reconcile the differences.

AJAX apps were great, but difficult for individual developers to write; as browsers kept changing, and people demanded apps on more devices, the code then became more and more complex and confusing. This challenge led to the rise of standard Javascript frameworks and libraries. JavaScript libraries arose to hide the complex browser differences, JavaScript frameworks made developing complex, powerful applications manageable.

1.4. APIs

Getting Data

We’ve looked at the concepts of Ajax and that it’s the technology we’ll use to add data to our project asynchronously. But where is this data coming from? And how do we get access to it? How will our app know how to retrieve that data.

We’ll be using an API to interact with various data sources.

What’s an API?

The acronym “API” stands for:

There’s data out there that’s just waiting to be used. Most of the data-rich applications you use get their data from 3rd party websites. They actually fetch this data using APIs. In the video below, Cameron will demo how Reddit uses APIs for added interactivity on its site.

Available APIs

There are tons of API’s available on the web that readily serve up interesting information and they’re usually free to use so long as you aren’t making thousands of requests per hour.

For instance Google offers a ton of API’s including Google Analytics, Google Maps, & Google Fonts.

ajax1-5

Looking around the web it isn’t hard to find examples of websites that use external API’s to create interesting interactions for users.

Reddit and the Reddit Enhancement Suite Chrome Plugin interact with a few different APIs to make reddit feel more dynamic.

When a user clicks on a link that resolves to a YouTube video, reddit opens up an embedded YouTube player which interacts with the YouTube API.

ajax1-6

When a user opens up a link to a Twitter feed, the reddit enhancement sweet chrome plug-in fires off an ajax request for twitter data.

It then uses the Twitter API to open up the tweet right here inside the page.

ajax1-7

Resources

1.5 XHR Async Request

It’s cake baking time! Now before you can make a cake you need all of the ingredients to build it. So, we have the cake mix, we have eggs, milk, and butter.

ajax1-8

So we have everything we need so I’ll turn on the oven and then we can start making it.

[Music plays while the cake batter is prepared.]

  1. Cake mix is put into a bowl
  2. Eggs added, milk added, butter added
  3. Ingredients are mixed into a batter
  4. Batter is poured into a pyrex container
  5. Pyrex container is put into the oven

Done! Okay, so work with me here a bit…

I need the cake to be baked before i can add frosting. And I’ve requested the oven to take the cake batter and turn it into a cake. When it’s done, the timer will beep letting me know that I can get the cake back.

But I don’t need to sit here and wait around for the cake to bake. I could go do something else. I could even make the frosting right now.

An XHR object is provided by the JavaScript environment and is used to make ajax requests. It’s like this initial prep stage of making the cake batter.

You have to manually do a lot of the steps to get the request set up and finally sent off. But then your code can continue on and do other things.

When the response does come back it’s been prepped to handle the return data.

With this analogy in mind let’s look at the XHR object in detail.

1.6 The XHR Object

Just like how the document boject is provided by the JavaScript engine, the JavaScript engine also provides a way for us to make asynchronous HTTP requests. We do that with an XMLHttpRequest object. We can create these objects with the provided XMLHttpRequest constructor function.

One of the best ways to learn is to get your hands dirty and try things out! So go to Unsplash, open up the developer tools, and run the following on the console:

const asyncRequestObject = new XMLHttpRequest();

Confusingly, the constructor function has “XML” in it, but it’s not limited to only XML documents.

Remember that the “AJAX” acronym used to stand for “Asynchronous JavaScript and XML”. Since the main file format that was originally used for asynchronous data exchange were XML files, it’s easy to see why the function is called XMLHttpRequest.

XMLHttpRequests (commonly abbreviated as XHR or xhr) can be used to request any file type (e.g. plain text files, HTML files, JSON files, image files, etc.) or data from an API.

Note: We’ll be digging into the XMLHttpRequest object. We’ll look at how to create it, what methods and properties need to be used, and how to actually send asynchronous requests. For even more info on using the XHR object to make async requests, check out these links:

1.7 XHR .open() method

So we’ve constructed an XHR object named asyncRequestObject. There are a number of methods that are available to us. One of the most important is the open method.

asyncRequestObject.open();

.open() takes a number of parameters, but the most important are its first two: the HTTP method URL to send the request

If we want to asynchronously request the homepage from the popular high-res image site, Unsplash, we’d use a GET request and provide the URL:

asyncRequestObject.open('GET', 'https://unsplash.com');

HTTP methods

The main two that you’ll be using are:

For more info, check out the Udacity course HTTP & Web Servers.

Warning: For security reasons, you can only make requests for assets and data on the same domain as the site that will end up loading the data. For example, to asynchronously request data from google.com your browser needs to be on google.com. This is known as the same-origin policy. This might seem extremely limiting, and it is!

The reason for this is because JavaScript has control over so much information on the page. It has access to all cookies and can determine passwords since it can track what keys are pressed. However, the web wouldn’t be what it is today if all information was bordered off in its own silos. The way to circumvent the same-origin policy is with CORS (Cross-Origin Resource Sharing). CORS is a technology that is implemented on the server. Services that provide APIs use CORS to allow developers to circumvent the same-origin policy and access their information.

Question 1 of 2

Go to Google, open up the developer tools, and run the following on the console:

const req = new XMLHttpRequest();
req.open('GET', 'https://www.google.com/');

What happens?

Why

The XHR’s .open() method does not actually send the request! It sets the stage and gives the object the info it will need when the request is actually sent.

A bit anti-climactic… So let’s actually send the request!

Question 2 of 2

An XHR object’s .open() method can take a number of arguments. Use the MDN XMLHttpRequest.open() documentation to explain what the following code does:

const myAsyncRequest = new XMLHttpRequest();
myAsyncRequest.open('GET', 'https://udacity.com/', false);
Why

Passing false as the third option makes the XHR request become a synchronous one.

This will cause the JavaScript engine to pause and wait until the request is returned before continuing - this “pause and wait” is also called “blocking”.

This is a terrible idea and completely defeats the purpose for having an asynchronous behavior. Make sure you never set your XHR objects this way!

Instead, either pass true as the 3rd argument or leave it blank (which makes it default to true).’

1.8 XHR’s .send() method

To actually send the request, we need to use the send method:

asyncRequestObject.send();

Let’s check out what happens:

ajax1-9

We just saw that running this code doesn’t do anything. Well, that’s not actually true because something does happen.

A request is actually sent. We can see this if we check out the network pane. We want to record network traffic so make sure dev tools is recording. The record button is red so I know it’s recording.

ajax1-10

If it’s not red then just click on it to turn the recording feature on.

Now if I come down here and run the request it actually sends. The request is shown below.

ajax1-11

Let’s stop recording for now.

If I select the request we can see its headers.

ajax1-12

Here’s the URL that we requested and the request method. We can also see a preview of the request and then the response pane contains the actual HTML of the response.

So even though nothing happens to the page or in the code, the request is actually sent.

Now, it’s a little pointless to make a request for something but then do absolutely nothing with it! Why would you order some cake and then not go to pick it up or not eat it?

Handling Success

To handle the successful response of an XHR request, we set the XMLHttpRequest onload property on the object to a function that will handle it:

function handleSuccess () {
  // the `this` value is the XHR object
  // this.responseText holds the response from the server
  console.log( this.responseText ); // the HTML of https://unsplash.com/
}

asyncRequestObject.onload = handleSuccess;

As we just saw, if onload isn’t set, the request does return… but nothing happens with it.

Handling Errors

You might’ve picked up that onload is called when the response is successful. If something happens with the request and it can’t be fulfilled, then we need to use the onerror property:

function handleError () {
  // the `this` value is the XHR object
  console.log( 'An error occurred 😞' );
}

asyncRequestObject.onerror = handleError;

As with onload, if onerror isn’t set and an error occurs, that error will just fail silently and your code (and your user!) won’t have any idea what’s wrong or any way to recover.

1.9 A Full Request

Here’s what we’ve built so far.

function handleSuccess () {
  console.log( this.responseText ); // the HTML of https://unsplash.com/
function handleError () {
  console.log( 'An error occurred.😞' );
}

const asyncRequestObject = new XMLHttpRequest();
asyncRequestObject.open('GET', 'https://unsplash.com');
asyncRequestObject.onload = handleSuccess;
asyncRequestObject.onerror = handleError;
asyncRequestObject.send();

APIs and JSON

Getting the HTML of a website is ok, but it’s probably not very useful. The data it returns is in a format that is extremely difficult to parse and consume.

It’s a lot easier if we get just the data we want in an easily formatted data structure. The format for this is JSON.

Instead of requesting the base URL for Unsplash, let’s create an app that pulls an image from Unsplash’s API and relevant articles from the New York Times.

When making a request from an API that returns JSON, all we need to do is convert that JSON response into a JavaScript object.

We can do that with JSON.parse();. Let’s tweak the onload function to handle a JSON response:

function handleSuccess () {
  // convert data from JSON to a JavaScript object
  const data = JSON.parse( this.responseText );
  console.log( data );
}

asyncRequestObject.onload = handleSuccess;

1.10 Project Walkthrough

This is the project that we’ll be building.

ajax1-13

There’s a form that lets you search for something you’re interested in. It displays an image and some articles about it.

ajax1-14

This is how it will work when it’s finished.

Now let me revert to the start of the project. This is what the project looks like right now.

ajax1-15

Currently it’s just a form.

Here’s the code for the HTML form and this is the part of the page that will get populated with the data from the response.

ajax1-16

Here in the JavaScript file we’re storing all of the necessary DOM elements and we have an event listener for the form.

ajax1-17

In here is where we’ll kick off the async requests. So let’s get going.

Download the Starter Code

The starter project is on GitHub: https://github.com/udacity/course-ajax. You can clone the project by running the following Git command in your terminal:

git clone https://github.com/udacity/course-ajax.git

Once you’ve cloned the project, you’ll notice that it has three separate folders:

  1. lesson-1-async-w-xhr
  2. lesson-2-async-w-jQuery
  3. lesson-3-async-w-fetch

Make sure to work on the files for the correct lesson. Since this is the first lesson, we’ll be working in the lesson-1-async-w-xhr directory.

Create Your Accounts

To complete these final steps, you’ll need accounts with Unsplash and The New York Times.

Unsplash
The New York Times

Unsplash Request

In our app, the variable searchedForText contains the text we’re interested in, and we’ll set the onload property to a function called addImage (which is a do-nothing function that we’ll flesh out in a moment). If we temporarily set searchedForText to “hippos”, the code for the XHR call to Unsplash is:

function addImage(){}
const searchedForText = 'hippos';
const imgRequest = new XMLHttpRequest();

imgRequest.open('GET', 
  `https://api.unsplash.com/search/photos?page=1&query=${searchedForText}`);
imgRequest.onload = addImage;
imgRequest.send()

…but if you try running this code, you’ll get an error.

Quiz Question

The request for Unsplash doesn’t work because it needs an HTTP header to be sent along. What is the XHR method to add a header to the request? Check out the XMLHttpRequest documentation on MDN for help!

1.11 Set Request Header

Unsplash.com Images

The XHR method to include a header with the request is setRequestHeader. So the full code needs to be:

const searchedForText = 'hippos';
const imgRequest = new XMLHttpRequest();

imgRequest.open('GET',
  `https://api.unsplash.com/search/photos?page=1&query=${searchedForText}`);
imgRequest.onload = addImage;
imgRequest.setRequestHeader('Authorization', 'Client-ID <client-id>');
imgRequest.send();

function addImage(){
  debugger; // break debugger at this point...
}

After the request returned successfully let’s pause inside the function to check out what’s been returned.

To do that let’s add a debugger inside the addImage() function.

ajax1-18

Now if I refresh the page and search for flamingo’s it’ll pause. The this value is the xhr object itself and the response is stored in responseText.

ajax1-19

This is a JSON response and shows all of the text. We can see this information a lot more easily in the network pane. This request returned 13 responses.

ajax1-20

So we need to:

  1. convert the response from JSON into a JavaScript object
  2. get the first image
  3. format the data
  4. add it to the page.
function addImage() {
  let htmlContent = '';
  const data = JSON.parse(this.responseText);
  const firstImage = data.results[0];

  htmlContent = `<figure>
    <img src="${firstImage.urls.regular}" alt="${searchedForText}">
    <figcaption>${searchedForText} by ${firstImage.user.name}</figcaption>
  </figure>`;

  responseContainer.insertAdjacentHTML('afterbegin', htmlContent);
}

This code will add a <figure> element with an image pointing to the image from unsplash and a caption of the person that took the photo.

It will add this inside the response container as the first element.

Now we also want to make sure that we handle the case if no images are returned. We add in a check to make sure there are some image results returned.

function addImage() {
  let htmlContent = '';
  const data = JSON.parse(this.responseText);
  
  if (data && data.results && data.results[0]) {
    const firstImage = data.results[0];
    htmlContent = `<figure>
      <img src="${firstImage.urls.regular}" alt="${searchedForText}">
      <figcaption>${searchedForText} by ${firstImage.user.name}</figcaption>
    </figure>`;
  } else {
    htmlContent = '<div class="error-no-image">No images available</div>';
  }
  
  responseContainer.insertAdjacentHTML('afterbegin', htmlContent);
}

If there aren’t any, then we’ll just display a message that there are no images.

1.11b NY Times Request

NY Times Articles

Since the New York Times doesn’t require a specific header, we don’t need to do anything special with adding a header.

We’ll set the onload property to the function addArticles that we’ll flesh out in a minute:

function addArticles () {}
const articleRequest = new XMLHttpRequest();
articleRequest.onload = addArticles;
articleRequest.open('GET',
  `http://api.nytimes.com/svc/search/v2/articlesearch.json?
    q=${searchedForText}&api-key=<your-API-key-goes-here>`);
articleRequest.send();

Make sure to fill in the URL above with the API key you received in an email from the New York Times after signing up as a developer.

Solution

I’ve added a debugger and searched for Lion.

If I go to the Network tab in DevTools I can click on the Ajax request. This will allow me to click on the Preview tab to the right and view the hierarchical data returned.

We can see that the response has a nested response object and inside that the docs property holds all of the articles.

ajax1-23

So we need to convert the response from JSON into a JavaScript object.

function addArticles() {
  let htmlContent = '';
  const data = JSON.parse(this.responseText);

  if (data && data.response.docs && data.response.docs[0]) {
    htmlContent = '<ul>' + data.response.docs.map(article =>
      `<li class="article">
        <h2><a href="${article.web_url}">${article.headline.main}</a></h2>
        <p>${article.snippet}</p>
      </li>`
    ).join('') + '</ul>';
  } else {
    htmlContent = '<div class="error-no-articles">No article available</div>';
  }
  
  responseContainer.insertAdjacentHTML('beforeend', htmlContent);
}

If some articles have been returned, we map over each article and then return a list item that contains the article’s headline and a snippet of the article.

Finally we combine all article list items together inside an unordered list tag. This gets added to the bottom of the page inside the response container.

ajax1-24

If there are no articles then it just displays the text no articles available.

1.12 Final Walkthough

let’s see our app in action.

Looking at the code one last time, we set up a listener for when the form is submitted. This listener contains the code below.

Our async requests are kicked off. One to unsplash.com and one to the New York Times.

When the unsplash request returns, it calls the addImage() function. When the New York Times request returns, it calls the addArticles() function.

Both of these functions convert the response from JSON, extract the data, and then add it to the page.

There are a number of moving parts to handle asynchronous requests but it’s pretty straight forward.

const imgRequest = new XMLHttpRequest();
imgRequest.open('GET', 
  `https://api.unsplash.com/search/photos?page=1&query=${searchedForText}`);
imgRequest.onload = addImage;
imgRequest.onerror = handleError;
imgRequest.setRequestHeader('Authorization', 'Client-ID <clinet-id-key>');
imgRequest.send();

function handleError(error) {
  console.log('An error occurred.😞');
  console.log('error:', error);
}

function addImage() {
  let htmlContent = '';
  const data = JSON.parse(this.responseText);
  
  if (data && data.results && data.results[0]) {
    const firstImage = data.results[0];
    htmlContent = `<figure>
      <img src="${firstImage.urls.regular}" alt="${searchedForText}">
      <figcaption>${searchedForText} by ${firstImage.user.name}</figcaption>
    </figure>`;
  } else {
    htmlContent = '<div class="error-no-image">No images available</div>';
  }
  
  responseContainer.insertAdjacentHTML('afterbegin', htmlContent);
}

const articleRequest = new XMLHttpRequest();
articleRequest.onload = addArticles;
articleRequest.open('GET', 
  `http://api.nytimes.com/svc/search/v2/articlesearch.json?
  q=${searchedForText}&api-key=<api-key>`);
articleRequest.send();

function addArticles() {
  let htmlContent = '';
  const data = JSON.parse(this.responseText);
  if (data && data.response.docs && data.response.docs[0]) {
    htmlContent = '<ul>' + data.response.docs.map(article =>
      `<li class="article">
        <h2><a href="${article.web_url}">${article.headline.main}</a></h2>
        <p>${article.snippet}</p>
      </li>`
    ).join('') + '</ul>';
  } else {
    htmlContent = '<div class="error-no-articles">No article available</div>';
  }

  responseContainer.insertAdjacentHTML('beforeend', htmlContent);
}

1.13 XHR Review

There are a number of steps you need to take to send an HTTP request asynchronously with JavaScript.

To Send An Async Request

To Use The Response

Note: The original XHR specification was created in 2006. This was version 1 of the specification. A number of years with minimal changes to the spec.

In 2012, work was started on a version 2 of the XHR specification. In 2014, the XHR2 spec was merged into the XHR1 spec so that there wouldn’t be diverging standards. There are still references to XHR2, but the XHR specification now fully incorporates XHR2.

Check out this HTML5Rocks article for info on the new tricks in XHR2 that are now in the XHR spec.

1.14 XHR Wrap-up

We just looked at using the XHR object to create and send asynchronous requests.

As you saw, there are a number of steps to create an XHR object, handle a successful request, and deal with errors.

It was a lot of code, but do we really need to write all of that code every single time we want to send an asynchronous request?

When using the XHR object the answer is yeah. But you don’t always have to use the XHR object to make async requests. You could use some third-party library like jquery to make the request for you.

Check out the next lesson to see how jquery makes async requests.

Lesson 2: Ajax w/ jQuery

2.1 JQuery & Ajax

Welcome back. So it turns out that the cake I made wasn’t the prettiest thing in the world. It should taste great, but it looks kind of awful.

ajax1-1

Totally not something I can show up with and be proud of having at a birthday party. So instead of me baking the cake and frosting it, let’s just have a professional do it.

With the original cake, I had to get all the ingredients, combine them myself, watch it while it’s in the oven, make the frosting, and then actually frost it. I had to do everything myself. It was just too much.

Now, I just requested the bakery to make it for me. They’re working on it now and will let me know when it’s ready to come back and pick it up.

So, I’m not doing all the work this time, but that doesn’t mean it’s not being done. Someone there is doing all of the same steps that I did in the previous lesson.

So when you use a JavaScript library like jQuery to make async requests, you just request the data and then jQuery does all of the hands-on work under the hood. Let’s check it out.

jQuery

jQuery is an incredibly popular JavaScript library that provides a lot of functionality right out of the box. It was created a number of years ago back when browsers hadn’t joined together to standardize on functionality. jQuery made life easier for developers that were building websites that had to function in all of the major browsers by providing a unified interface. The developer would use jQuery-specific functions and then jQuery would figure out what code to run depending on the browser that was being used.

jQuery is just JavaScript, so you’d download a current version and link to it with a regular <script> tag. Once it’s been included it on the page, you’ve got this powerhouse of functionality right at your fingertips.

Now that browsers have pretty much aligned, jQuery’s usage is not as necessary as it was several years ago. However, one powerful tool that it provides is it’s ajax() method. As its name suggests, jQuery’s ajax() method is used to handle all asynchronous requests.

Let’s see it in action.

Wanna Learn jQuery

jQuery is incredibly popular and used by hundreds of thousands of sites. If you want dig deeper into this cool JavaScript library, check out Udacity’s Intro to jQuery course.

2.2 jQuery ajax() Method

The .ajax() method is at the heart of all asynchronous requests for the entire jQuery library. There are a couple of ways you can call the .ajax() method:

$.ajax(<url-to-fetch>, <a-configuration-object>);

// or 

$.ajax(<just a configuration object>);

The most common way to use the .ajax() method is with just the configuration object, since everything can be set inside the configuration object.

What’s a “configuration object”

A configuration object is just a plain ol’ JavaScript object that’s used to configure something. For example:

var settings = {
   frosting: 'buttercream',
   colors: ['orange', 'blue'],
   layers: 2,
   isRound: true
};

…the settings configuration object can be used in the imaginary MakeCake constructor function:

const myDeliciousCake = MakeCake( settings );

Alternatively, the settings object could be passed in directly:

const myDeliciousCake = MakeCake({
   frosting: 'buttercream',
   colors: ['orange', 'blue'],
   layers: 2,
   isRound: true
});

Making an Ajax call

jQuery’s .ajax() method has to be incredibly versatile and powerful if it’s what powers all of jQuery’s asynchronous requests. A simple Ajax request would look like this:

$.ajax({
    url: 'https://swapi.co/api/people/1/'
});

Let’s test it out!

  1. go to the jQuery website
  2. open up your browser’s developer tools
  3. make sure the network traffic is being recorded
    • in Chrome, switch to the network pane
  4. add the request above to the console
  5. …and run it!

swapi request Running an asynchronous request in the console. The request is for a resource on SWAPI. The request is displayed in the network pane.

So we can make a request with .ajax(), but we haven’t handled the response yet.

2.3 Handling Returned Data

If you recall from setting up an XHR object, the response was handled by a function. It’s the same thing with the .ajax() method.

We can chain onto .ajax() with a .done() method. We pass the .done() method a function that will run when the Ajax call is done!

function handleResponse(data) {
    console.log('the ajax request has finished!');
    console.log(data);
}

$.ajax({
    url: 'https://swapi.co/api/people/1/'
}).done(handleResponse);

swapi request with done Asynchronous call set up with a done method to handle the response. The request is made, and then the response is logged to the console.

Let’s convert the existing, plain XHR call with jQuery’s .ajax(). This is what the app currently has:

const imgRequest = new XMLHttpRequest();
imgRequest.onload = addImage;
imgRequest.open('GET',
  `https://api.unsplash.com/search/photos?page=1&query=${searchedForText}`);
imgRequest.setRequestHeader('Authorization', 'Client-ID <client-id-here>');
imgRequest.send();

A lot of this information is handled behind the scene by jQuery, so here’s the first step in the conversion:

$.ajax({
  url: `https://api.unsplash.com/search/photos?page=1&query=${searchedForText}`
}).done(addImage);

With the jQuery code:

Quiz Question

The only change that needs to be made is including the Client ID header along with the request so that Unsplash will verify the request.

Why don’t you check out the API for the .ajax() method and select the code below that correctly adds an “Authorization” header to the request.

The request should send perfectly now. Fantastic work! But there seem to be issues with the response and how it’s handled.

2.4 Success Callback Cleanup

Right now, content isn’t getting added to the page. We need to clean-up the addImage() function. which gets called on success which is now the .done() method.

jQuery detects the response and if it’s JSON, it will automatically convert it to JavaScript for us. How awesome is that!

So we only need to make a few tweaks to the existing code.

Here’s what it currently is:

function addImage() {
  const data = JSON.parse(this.responseText);
  const firstImage = data.results[0];

  responseContainer.insertAdjacentHTML('afterbegin', `<figure>
      <img src="${firstImage.urls.small}" alt="${searchedForText}">
      <figcaption>${searchedForText} by ${firstImage.user.name}</figcaption>
    </figure>`
  );
}

Now we need to change the first three lines from above to become this:

function addImage(images) {
  const firstImage = images.results[0];

  responseContainer.insertAdjacentHTML('afterbegin', `<figure>
      <img src="${firstImage.urls.small}" alt="${searchedForText}">
      <figcaption>${searchedForText} by ${firstImage.user.name}</figcaption>
    </figure>`
  );
}

What changed

The code that adds the HTML to the response container hasn’t changed at all!

2.4b NY Times Request

Replace NY Times XHR With $.Ajax()

Now that we’ve walked through converting one request from using XHR to jQuery’s .ajax() method, why don’t you give it a shot on your own and convert the second request!

Make sure to use the existing code as an example. If you get stuck, check out the documentation page.

When you’re successfully converted the code to use jQuery’s .ajax() method and fixed the callback function so it adds the data to the page, check the checkbox to continue.

2.5 Code Walkthrough

Here’s the completed project that uses jQuery’s Ajax method to asynchronously fetch the data.

Setting up the XHR object has been replaced with calls to $.ajax(), and the data is passed in directly as an object.

When the response is returned it’s handled by the .done() method.

If there are any errors, then the .fail() method will display an error message letting the user know that content couldn’t be loaded.

$.ajax({
  url: `https://api.unsplash.com/search/photos?page=1&query=${searchedForText}`,
  headers: {
    Authorization: 'Client-ID <client-id-here>'
  }
}).done(
  addImage
).fail(function (err) {
  requestError(err, 'image');
});

function addImage(data) {
  let htmlContent = '';

  if (data && data.results && data.results.length > 1) {
    const firstImage = data.results[0];

    responseContainer.insertAdjacentHTML('afterbegin', `<figure>
        <img src="${firstImage.urls.regular}" alt="${searchedForText}">
        <figcaption>${searchedForText} by ${firstImage.user.name}</figcaption>
      </figure>`
    );
  } else {
    htmlContent = '<div class="error-no-image">No images available</div>';
  }

  responseContainer.insertAdjacentHTML('afterbegin', htmlContent);
}

$.ajax({
  url: `http://api.nytimes.com/svc/search/v2/articlesearch.json?
    q=${searchedForText}&api-key=<api-key-here>`
}).done(
  addArticles
).fail(function (err) {
  requestError(err, 'articles');
});

function addArticles(data) {
  let htmlContent = ''

  if (data.response && data.response.docs && data.response.docs.length > 1) {
    htmlContent = '<ul>' + data.response.docs.map(article =>
      `<li class="article">
        <h2><a href="${article.web_url}">${article.headline.main}</a></h2>
        <p>${article.snippet}</p>
      </li>`
    ).join('') + '</ul>';
  } else {
    htmlContent = '<div class="error-no-articles">No articles available</div>';
  }

  responseContainer.insertAdjacentHTML('beforeend', htmlContent);
}

function requestError(e, part) {
  console.log('An error occurred.😞');
  console.log('error:', e);
  responseContainer.insertAdjacentHTML('beforeend',
    `<p class="network-warning error-no-${part}">Missing ${part}</p>`);
}

Since unsplash returns a JSON object, the response will automatically get converted to JSON by jQuery.

Similarly, the New York Times request returns JSON data that will get converted to a regular JavaScript object.

Pretty cool how jQuery handles most of the set up for us, isn’t it?

Using jQuery’s .ajax() method, there’s less setup code that you need to manage. That’s good, but to use jQuery we also have to include the entire library and force our users to download the entire thing every time.

It’s true that they might have it cached, but do we really need jQuery? Is it doing something special?

2.6 Peek inside $.ajax()

On the left is the cake from the previous lesson and on the right is the one that I just got back from the store.

With this one, I did all of the work myself. I added all the data, I set all the properties, and I hooked everything together.

With the store bought cake, I didn’t do any of that work, but someone else did. They had to go through all of the same steps that I did.

So even though I’m calling it jQuery’s.ajax method, somewhere in that code jQuery is creating an XHR object, setting the right properties and methods, and then sending off the request.

Examine jQuery

We’re about to dig into jQuery’s source for a second. To do that, you need to:

  1. open up the project in a browser
  2. open up DevTools
  3. switch to the “Sources” pane
  4. open up the jquery.js file
    • in Chrome, you can open a specific file by searching for it with ctrl/command + P
  5. set a breakpoint on line 9036

jquery-xhr-set-breakpoint A breakpoint set in the jQuery source file right where new XMLHttpRequest is created

Search For A Topic

Now that we’ve added the breakpoint, if we do a search, the JavaScript code will run (which will use jQuery’s .ajax() method!) and DevTools will pause when it hits the line with the breakpoint.

So go ahead and do a search to cause the code to break at the breakpoint. Why not search for something exciting like “Volcanoes”!

ajax1-27 DevTools paused at breakpoint

Debugging in Chrome

TIP: If you’ve never done it before, debugging is a JavaScript application can seem like a complicated process. We’ll be looking at the important parts of DevTools in this course, but if you’re looking for a deeper dive, check out the following resources on Google’s Developer site:

2.7 Review Call Stack

The DevTools has a ton of helpful information! If you’re not familiar with them, you really should spend some learning about all of its features. It’ll make developing and debugging websites a lot easier!

One helpful piece of info that DevTools provides is the JavaScript Call Stack. This displays the order of function calls that are in progress.

The function at the bottom of the stack is the first one to run. It calls the second one on the stack…the second calls the third, the third… you get the idea. A function stays on the stack until the one above it returns.

We can click on the bottom function in the stack (the anonymous function) to see that what kicked all this code off was the $.ajax() call for the Unsplash images. That $.ajax() call in turn calls transport.send(), which calls options.xhr(), which creates a new XMLHttpRequest() object!

So the order is:

  1. our code in an anonymous function calls .ajax()
  2. .ajax() calls a .send() method
  3. .send() calls options.xhr()
  4. options.xhr() calls jQuery.ajaxSettings.xhr which creates a new XHR object

jquery-xhr-set-breakpoint Clicking through the call stack to see the order of function calls

Question 1 of 2

When $.ajax() is called, does the jQuery code create a new XHR object each time or does it create an initial one and reuses it for each subsequent call to .ajax()?

Look at jQuery’s code and especially the jQuery.ajaxSettings.xhr function.

jQuery.ajaxSettings.xhr = function() {
  try {
    return new window.XMLHttpRequest();
  } catch ( e ) {}
};

We can see that the code is return new window.XMLHttpRequest();. So this code will return a new XHR object every time it’s called (which happens every time $.ajax() is run!).

Question 2 of 2

Try working through the .send() function (the third item from the bottom of the call stack) on your own to see how it sets up the newly created XHR object. After reviewing the code, how does it set all of the headers?

for ( i in headers ) {
  xhr.setRequestHeader( i, headers[ i ] );
}

jQuery uses a for…in loop to iterate over the data in the headers object. This can be seen on lines 9094-9096.

2.8 .ajaxTransport Walkthrough

Here’s the send function which is the third item in the call stack.

ajax1-28

This call creates a new xhr object and stores it in an appropriately named xhr variable.

It then immediately calls the xhr.open method, passes in the data that we’ve provided in the configuration object.

It then makes its way down to the headers and then loops over each one calling the setRequestHeader method.

ajax1-29

This code here sets up the callback function.

ajax1-30

After the callbacks for onload, onerror, & onabort are set up the xhr.send() method is finally run to send the request.

2.9 Other Async Methods

jQuery has a number of other methods that can be used to make asynchronous calls. These methods are:

Each one of these functions in turn calls jQuery’s main .ajax() method.

These are called “convenience methods” because they provide a convenient interface and do some default configuration of the request before calling .ajax().

Let’s look at the .get() and .post() methods to see how they just call .ajax() under the hood.

Add a Breakpoint

With the project open in a browser:

  1. load up DevTools
  2. open the Sources pane
  3. open the jQuery file
  4. add a breakpoint to line 8797
  5. reload the page (this will pause the code at the breakpoint you just made!)

jquery-xhr-set-breakpoint A breakpoint is added to jQuery source file in DevTools. Be sure to set this breakpoint at 8797 rather than 9036 shown here.

The first time through the loop, the method variable will be get. This makes

jQuery[ method ] = function( ... ) { ... }

become

jQuery[ 'get' ] = function( ... ) { ... }

which gives us the $.get() method!

On line 8807 you can see that this new jQuery[ 'get' ] function returns a call made to jQuery.ajax( ... ).

Notice that before the .ajax() call is run, the type property is set to the method variable (which is still ‘get’). So calling $.get() calls $.ajax() with some preset properties.

This exact same code runs right after this for ‘post’. The code creates a jQuery[ 'post' ] function that will call jQuery.ajax( ... ) and set the type property to ‘post’.

Isn’t it pretty cool how jQuery provides these convenience methods that just end up calling the main .ajax() method!?

jquery get & post methods Walking through the jQuery source to see how the $.get() and $.post() methods are created. This sets some default properties, and then runs $.ajax()

Which Method Should You Use?

From the jQuery website:

It’s often considered good practice to use the $.ajax() method over the jQuery provided convenience methods.

2.10 jQuery Async Wrapup

In this lesson, we used the jQuery library to make asynchronous requests and we use jQuery’s .ajax() method to do it.

We also dove into the .ajax() method and saw that deep in the underbelly of the code, it just did the following.

  1. It created an XHR object
  2. Set all the properties and callbacks
  3. Then sent off the request for us.

So we’ve seen how to make async requests with XHR, and now we just looked at using a middleman to handle the groundwork for us.

In the next lesson, we’ll look at the shiny new way to make async requests.

It’ll be gluten-free, carb-free and even sugar-free, will it even be a cake at this point? Who even knows?

Lesson 3: Ajax with Fetch

3.1 Fetch Intro

So we have a couple of cakes already but we’d like to make another cake.

ajax1-1

But we’ve learned from lesson one that if I make the cake, it’ll be downright ugly.

If I let a professional make it, then it’ll look amazing, but it’ll be expensive.

This next cake needs the love and care from the first cake and the quality of the second cake.

My friend Miriam is a baker. Asking Miriam to bake a cake for me is just like the new technology we’ll be looking at in this lesson.

I’m not doing the work, I just make the request, and I don’t have to load some professional library like jQuery because this new tool is provided right in the browser.

So, what is this life-changing tech? It’s called Fetch API.

The name may not be all that impressive-sounding, but Fetch really is an amazing tool that will change how you write asynchronous requests. Let’s see how it works.

3.2 What is Fetch

Fetch is the new way to make network requests! After looking at all of the manual setup that needs to go into setting up an XMLHttpRequest, you might be feeling that a lot of complexity went into making a simple request.

If all I want is an image from Unsplash, why do I need to do all this setup before I can even make the request? I just want an image file, so let me just ask for the file without having to drill through the unnecessarily complicated XHR spec.

Fetch is a new API that was built to make requesting resources (primarily across a network) a whole lot easier. One thing that makes the new Fetch API a lot nicer than the old XHR way of doing things is that Fetch is promise-based!

Hopefully you’re sold that Fetch is the way of the future for making requests, so let’s see it in action!

⚠️ Fetch Is Promise-based ⚠️

As mentioned above, the new Fetch API utilizes Promises under the hood. If you’ve got a handle on how Promises work, then give yourself a pat on the back then skip down to the next section.

If the word “Promises” makes you feel a little queasy and unsure of your life’s future, don’t panic! Breathe! Then head over to our short course on JavaScript Promises to level up your JavaScript muscles.

⚠️ Might Need A Polyfill ⚠️

Check out http://caniuse.com/#feat=fetch to see if your browser supports this awesome new API!

If your browser is not supported(IE or Opera Mini), just add this polyfill to your project, so you can start using Fetch even without your browser supporting it!

3.3 Fetch Request

Write The Fetch Request Ok, let’s look at a sample fetch request, and then we’ll make a fetch request for an image from Unsplash.

fetch('<URL-to-the-resource-that-is-being-requested>');

So yeah…that’s it. In it’s smallest form, a Fetch request is just the fetch() function and a string to the resource that’s being requested. It’s just so short and easy to read (sigh I think I’m in love!). Let’s take a peek at what a real request looks like:

fetch('https://api.unsplash.com/search/photos?page=1&query=flowers');

fetch(‘https://api.unsplash.com/search/photos?page=1&query=flowers’); If you try running this Fetch request on the console, then you should get a Promise returned to you.

fetch request A fetch request being run on the console of Unsplash’s website. The fetch request returns a Promise.

💡 Cross-Origin Issues? 💡

Did you just try running the Fetch request and it didn’t work? Were you running it on Unsplash’s website? If not, make sure you go to https://unsplash.com/, open the console, and try running it from there.

Just because Fetch is new and awesome and is replacing the XHR object for making asynchronous network requests doesn’t mean it can bypass the rules for making those network requests. Fetch requests still need to obey the cross-origin protocol of how resources are shared.

This means that, by default, you can only make requests for assets and data on the same domain as the site that will end up loading the data.

Remember that Unsplash requires an Authorization header to make a request through its API. Check out these links on Fetch’s documentation to see how to add an Authorization header to the Fetch request.

Question 1 of 2

Docs are a dev’s best friend! Take a quick look through them and pick the correct way(s) to add a header to a Fetch request from the options below. Also, instead of cheating and guessing, try testing out the code you think is correct in your app or on the console to see how it runs!

Solution

The correct answers are options 2 and 3. The Fetch request takes the URL to the requested resource as the first argument, but the second argument is a configuration object. One of the options to this config object is a headers property.

One of the new additions that rode along on the coattails of Fetch is a new Headers constructor function. The headers property of a Fetch request’s configuration object can either be a plain object of headers to include, or it can be a Headers object that’s been built up with headers.

Question 2 of 2

What do you think the default HTTP method is for a Fetch request? Why don’t you try running a Fetch request and look in the DevTools to see the HTTP method that is used.

Check out these links from the specification to find out more:

Changing The HTTP Method

So the default HTTP method for a Fetch request is the GET method. We can choose a different HTTP method by passing a method property in the configuration object:

fetch(`https://api.unsplash.com/search/photos?page=1&query=${queryText}`, {
  method: 'POST'
});

This will send the request with the POST HTTP header.

Fetch’s specification does not limit what HTTP methods can be used, although it does recommend that all methods are written in uppercase for consistency with the HTTP Verbs specification.

3.4 Handle the Response

Handle The Response Ok, you’ve learned about making a Fetch request, and you’ve sent a few of them off…but nothing happened because we didn’t tell our code to handle the response. Let’s get our code ready to handle the response.

Remember that Fetch is Promise-based. This means that when we fire of the Fetch request, it will automatically return a promise that we can use to listen for the response.

💡 Javascript Promises Reminder 💡

Dealing with the returned data from a Fetch request is all done with Promises, so if any of this looks confusing or you don’t know how .then() works or why it’s needed, check out our course on JavaScript Promises.

Since a Fetch request returns a Promise, then all you have to do is call .then() on that Promise.

fetch(`https://api.unsplash.com/search/photos?page=1&query=${queryText}`, {
  headers: {
    Authorization: 'Client-ID abc123'
  }
}).then(function(response) {
  debugger; // work with the returned response
});

If you haven’t already, put the code above in our JavaScript file and search for something. Because we added a debugger line inside the .then() function, the code will pause on the debugger line when the response is finally returned.

fetch request Browser showing the app with DevTools loaded. A search for “trees” is made. The browsers pauses at the debugger line. The response is a Response object.

Quiz Question

We’ve successfully made the request, and you should be able to see the response in your console. Which property has the actual JSON data of the images?

3.5 The Response Object

See how the response returned is of type Response? This Response object is new with the Fetch API and is what’s returned when a Fetch request resolves.

Ok, so that’s great and all, but did you notice anything weird about the response object? It didn’t have any of the data that we searched for!

That’s because a response object has information about the response itself, it doesn’t have the data…yet. To actually get the data, we need to get the “body” of the response.

Since the Unsplash API we’re using will return JSON to us, we just need to call .json() on the response variable.

fetch(`https://api.unsplash.com/search/photos?page=1&query=${queryText}`, {
  headers: {
    Authorization: 'Client-ID abc123'
  }
}).then(function(response) {
  return response.json();
});

The .json() method on a Response object returns a Promise, so we need to chain on another .then() to actually get and start using the returned data. This time, let’s call addImage to pass it the returned data:

fetch(`https://api.unsplash.com/search/photos?page=1&query=${queryText}`, {
  headers: {
    Authorization: 'Client-ID abc123'
  }
}).then(function(response) {
  return response.json();
}).then(addImage);

function addImage(data) {
  debugger;
}

There are a number of methods on a Response object. Each one will let the code handle different response types.

For example, the .json() method that we’ve looked at will take the response and convert it to JSON. What happens if we requested an image, instead?

Quiz Question

Which of the following methods should be used if you wanted to fetch an image?

If you get stuck, check out:

3.6 ES6 Arrow Function

You might be thinking that this Fetch request is starting to look like a lot of code…and it is. One quick way to shrink the amount of code is to use an ES6 Arrow function!

We can convert the first .then() function which gets the response, calls the .json() method on it, and returns a Promise, all to a single line with an Arrow function.

This

}).then(function(response) {
  return response.json();
})

becomes this.

}).then(response => response.json())

So the new request would be:

fetch(`https://api.unsplash.com/search/photos?page=1&query=${queryText}`, {
  headers: {
    Authorization: 'Client-ID abc123'
  }
})
.then(response => response.json())
.then(addImage);

function addImage(data) {
  debugger;
}

If Arrow functions are new to you, check out Udacity’s ES6 course!

fetch request Browser showing the app with DevTools loaded. A search for “trees” is made. The browsers pauses at the debugger line within the addImage function. The response is a Response object.

3.7 Render & Error Handling

Display The Image On The Page

We’re making our request to Unsplash, it’s returning a response that we’re then converting to JSON, and now we’re seeing the actual JSON data.

Fantastic! All we need to do now is display the image and caption on the page.

Here’s the code that I’m using:

function addImage(data) {
  let htmlContent = '';
  const firstImage = data.results[0];

  if (firstImage) {
    htmlContent = `<figure>
      <img src="${firstImage.urls.small}" alt="${searchedForText}">
      <figcaption>${searchedForText} by ${firstImage.user.name}</figcaption>
    </figure>`;
  } else {
    htmlContent = 'Unfortunately, no image was returned for your search.'
  }

  responseContainer.insertAdjacentHTML('afterbegin', htmlContent);
}

This code will:

Handling Errors

Our app is almost done with getting the image from Unsplash!

We’re requesting the image and adding it to the page, but this is only one possible outcome. Granted, it’s the most likely way that the app will end up, but we’re not handling any errors.

What errors could possible happen you ask? A couple I can think of are:

We’re handling this last one in the addImage function. For the other two, we can use chain on a .catch() method to the Fetch request!

Again, because a Fetch request returns a Promise .catch() is available to us from the Promise API.

So let’s add a .catch() method to handle errors:

fetch(`https://api.unsplash.com/search/photos?page=1&query=${queryText}`, {
    headers: {
        Authorization: 'Client-ID abc123'
    }
})
.then(response => response.json())
.then(addImage)
.catch(e => requestError(e, 'image'));

function addImage(data) {
  debugger;
}

function requestError(err, part) {
  console.log(err);
  responseContainer.insertAdjacentHTML('beforeend',
    `<p class="network-warning">
      Oh no! There was an error making a request for the ${part}.
    </p>`);
}

This code adds the requestError function and adds a .catch() request to the end of the Promise chain.

The .catch() function will receive an error object (that we’re storing in the err variable) and in turn calls requestError passing along the error object and the request that failed.

If the Promise rejects anywhere along the line, the requestError function will log the error and display a warning message to the user that the request failed for some reason.

3.8 NY Times Request

The app is now able to handle searching for images and displaying images! Woohoo!

Now see if you can follow these same steps to get the New York Times articles to display. When you can perform a search in your app and have both an image and some New York Times articles display, check the checkbox below.

This might seem like a lot, but we looked in detail at how to make a Fetch request and how to handle the response. You’re basically going to follow the exact same steps for this request. You’ll need to: make the request to the NYTimes API convert the response to JSON loop through the JSON data and add the articles to the page

Bonus points if you use .catch() to handle any network errors!

Solution

The first fetch makes a request to unsplash’s API for an image search. We pass along the required headers, convert the response to JSON, and then pass the actual JSON data off to the addImage function.

If there’s a problem with the request, then the .catch will receive the error and call the requestError function with the error and that it was the image request that failed.

fetch(`https://api.unsplash.com/search/photos?page=1&query=${queryText}`, {
  headers: {
    Authorization: 'Client-ID abc123'
  }
})
.then(response => response.json())
.then(addImage)
.catch(err => requestError(err, 'image'));

fetch(`http://api.nytimes.com/svc/search/v2/articlesearch.json?
    q=${queryText}&api-key=abc123`)
.then(response => response.json())
.then(addArticles)
.catch(err => requestError(err, 'articles'));

This makes a request for the New York Times articles and it’s very similar to the fetch request for unsplash. It sends the request to the New York Times API and it converts the response to JSON and passes the JSON data to the addArticles function.

If there’s a problem with this fetch request, the catch method will also call the requestError function, passing it the error and that it was the articles requests that failed.

We looked at the addImage function earlier, so let’s get down to the addArticles function.

function addArticles(data) {
  let htmlContent = ''

  if (data.response && data.response.docs && data.response.docs.length > 1) {
    htmlContent = '<ul>' + data.response.docs.map(article => `
      <li class="article">
        <h2><a href="${article.web_url}">${article.headline.main}</a></h2>
        <p>${article.snippet}</p>
      </li>`
    ).join('') + '</ul>';
  } else {
    htmlContent = '<div class="error-no-articles">No articles available</div>';
  }
  responseContainer.insertAdjacentHTML('beforeend', htmlContent);
}

This code will loop over all of the articles, adding them to an unordered list. If there aren’t any articles, then it displays this error message.

And that’s basically all there is to our app.

My Code Solution

fetch(`https://api.unsplash.com/search/photos?page=1&query=${queryText}`, {
  headers: {
    Authorization: 'Client-ID abc123'
  }
}).then(response => {
  if (response.ok) {
    return response.json();
  }
  console.log(response.status, response.statusText);
  throw new Error('network error');
}).then(jsonData => {
  console.log(jsonData);
  return jsonData;
}).then(addImage)
  .catch(err => requestError(err, 'image'));

fetch(`http://api.nytimes.com/svc/search/v2/articlesearch.json?
  q=${queryText}&api-key=abc123`
).then(response => {
  if (response.ok) {
    return response.json();
  }
  console.log(response.status, response.statusText);
  throw new Error('network error');
}).then(jsonData => {
  console.log(jsonData);
  return jsonData;
}).then(addArticles)
  .catch(err => requestError(err, 'articles'));

3.9 Fetch Wrap-up

We just looked at making requests using the fetch API. The new fetch API is awesome in so many ways.

You can make a complicated request involving custom headers and caching or a simple request using just the URL to the resource.

There are also new headers, requests, and response interfaces. On top of this, Fetch is promise based, so it naturally helps you create easy to write asynchronous calls in your code.

In the past, making asynchronous calls has been somewhat tedious, but thankfully with the new fetch API, things have gotten a whole lot easier.

3.10 Course Wrap-up

Thanks for joining me as we progressed through the different ways to make AJAX requests.

You now have three tools up your sleeve whenever you need to make async requests.

  1. You can make requests using the tried and true but cumbersome XHR
  2. You can use the jQuery library to smooth out making async requests
  3. You can use the Fetch API to make promise-based asynchronous requests

Using these tools, you can now make dynamic requests from your Javascript that don’t force a web page refresh and provide an overall better experience for your users.