DEV Community

Cover image for Fetch API is new old version of AJAX
Diona Rodrigues
Diona Rodrigues

Posted on • Updated on • Originally published at dionarodrigues.dev

Fetch API is new old version of AJAX

If you started your developer career about a decade ago, you probably know how complicated it was to use AJAX in our web applications. However, by now we all know that modern vanilla JavaScript introduces us to several powerful functionalities, including the Fetch API - an easier way to get data from the server without reloading the page.

The term AJAX (Asynchronous JavaScript and XML) emerged in the late 1990s as a programming concept that allowed parts of the HTML DOM to be updated without completely refreshing the page, making our projects more dynamic and interactive. And it was made possible by the XMLHttpRequest API (introduced by Internet Explorer 5 in 1998). As its name says, this API was designed to get XML via HTTP - and some time later it added support for other formats like JSON, HTML and plain text. But working with XMLHttpRequest was a pain back then, and because of that some libraries like jQuery (created in 2006) abstracted it to make it easier to use.

Following the evolution of JS, the Fetch API was introduced in 2015 and has become the standard for handling data requests today. The Fetch API is now working on the Node.js side as well (version 21) as you can read in this LogRocket post and in the official Node.js announcement.

Before we jump into the Fetch API, let’s understand some JavaScript concepts (for a complete explanation, see the references in the end of this article):

What is async in JavaScript?

By default, JavaScript works synchronously as a single-threaded programming language, which means that one process must complete before another starts. However, there are a few ways to ensure processes work together at the same time in JS by making our code asynchronous.

Async Javascript Illustration

For ease of understanding, let's imagine that you go to a pizzeria to order a pizza. The attendant takes your order and delivers it to the kitchen. Synchronously, the attendant have to wait until your pizza is cooked, bring it to you, and then go to another customer to take another order, waiting for their pizza get ready and so on. As you can see, it’s not a good approach. On another hand, asynchronously, the attendant doesn’t need to wait for the pizza get ready to move to the next customer, they just simply moves to another customer taking more orders and delivering them to the kitchen as the pizzas get ready to be delivered to customers. This is the difference between a synchronous and asynchronous systems.

Callback functions

One of the oldest and most common ways of dealing with asynchronous code in JS is to use callback functions. It's a JS concept to say a function: "wait to run in the future, not now".

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

There are many ways to implement this, for example when we need to get some external data and wait for the server to respond then do another thing. This is where AJAX comes into play, and jQuery makes it easy by using its library's $.ajax() method.

But the problem with callbacks starts when we need to nest them to execute different processes - which is called callback hell - making our code very difficult to read and maintain.

Promise in JS

ES2015 introduced Promise, a modern alternative to avoid callback hells.

"The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value." -MDN

In a nutshell, a promise acts as a proxy for an unknown value that will eventually become available at some point, ending up as a resolved state if everything goes well, or a rejected state if something goes wrong.

Using a Promise we call .then() to be executed if the result is resolved, or .catch() if it is rejected. In addition we can also chain Promises, making one Promise return another Promise. Have a look at the example below:

let myPromise = new Promise(function(myResolve, myReject) {
  // "Producing Code" (May take some time)

  myResolve(); // when successful
  myReject(); // when error
});

// "Consuming Code" (Must wait for a fulfilled Promise)
myPromise.then(
  function(value) {
    /* code if successful */
  },
  function(error) {
    /* code if some error */
  }
);
Enter fullscreen mode Exit fullscreen mode

The example above is from W3Schools.

What’s Fetch API in JavaScript and how to use it?

Before any explanation about Fetch API, I want to show you its syntax:

fetch('/https://api.github.com/users/dionarodrigues');
Enter fullscreen mode Exit fullscreen mode

The code above will simply make an HTTP request to the GitHub API domain to get some JSON data. What a wonderful and concise code, isn't it!

Also introduced in ES2015, the Fetch API emerged as a modern successor to XMLHttpRequest: the fetch() method takes a path to a resource as input and returns a Promise, making it possible to execute a function if resolved or rejected.

fetch('https://api.github.com/users/dionarodrigues')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.log(err));
Enter fullscreen mode Exit fullscreen mode

In the example above you can see the fetch() method working together with .then() and .catch() as the Fetch API uses Promise behind the scenes:

  1. Make an HTTP request to the GitHub API domain
  2. If resolved, convert it into JSON data by using .json() method
  3. As .json() method returns another Promise, if resolved, display the result in the console.
  4. In case any of the steps above fail, display the error in the console

Async/Await and Fetch API

Async/Await was introduced in ES2017 and works as a syntactic sugar for promises, making it possible to work with asynchronous functions.

See the code and its description below to better understand how to implement Async/Await with Fetch API:

async function getUserData() {
  try {
    const response = await fetch('https://api.github.com/users/dionarodrigues');
    const data = await response.json();
    console.log(data);
  } catch (err) {
    console.log(err);
  }
}
Enter fullscreen mode Exit fullscreen mode

The code above works on the same way that the previous one. The difference here is that we no longer need to use promise syntax, instead of it we use a wrapper to restyle the promise, making it easier to read and use. We use the keyword async to make this function asynchronous and await to block the execution of code inside the asynchronous function until the process terminates. Then we use try/catch to handle the resolved and rejected status.

Another way to use try/catch with async functions is to handle the catch outside the function - when it runs:

async function getUserData() {
    const response = await fetch('https://api.github.com/users/dionarodrigues')
    const data = await response.json()
    console.log(data)
}

getUserData.catch(err => console.log(err))
Enter fullscreen mode Exit fullscreen mode

It is important to highlight that this will not catch server errors such as 404 (not found) or 500(internal server error), for example, only errors caused by network or CORS issues. To learn more about how to deal with all types of errors when using the Fetch API, I recommend you read this other article called "Fetch API, do you really know how to handle errors?".

References

Conclusion

In the early days of the web, AJAX concept was strongly used along with XMLHttpRequest API. Nowadays we have a better and modern way to handle data asynchronously by using Fetch API. I hope you understood some important JavaScript concepts and how to implement fetch() in this article. Feel free to make any questions and don’t stop here, take a look at the references above to a better understanding.

See you next time. 😁

Top comments (25)

Collapse
 
camco profile image
Camco

Super helpful

Collapse
 
dionarodrigues profile image
Diona Rodrigues

Thank you!

 
vetras profile image
vetras

Well, you are not wrong because this is pretty much a personal preference.
I just do not agree with you. 😎

Hiding async code with syntax sugar
It's not hiding, it's making it clear that it is async.

with async/await you have to wrap stuff in a try/catch
Only if you can handle the error and know what to do, otherwise it is better to let it bubble up.

Lastly, all the possible options you mention although correct, I think they miss the point.

If you have this:

fetch("someapi.com")
    .then(foo);
bar();
Enter fullscreen mode Exit fullscreen mode

The point is to refactor into async/await but keep the exact same behaviour.
It does not matter much what that behaviour is on the example (functions don't even have a body here).

Cheers! 🍻 happy coding

Collapse
 
vetras profile image
vetras

I don't think it is easier to read. Besides if you have this:

fetch("someapi.com")
    .then(foo);
bar();
Enter fullscreen mode Exit fullscreen mode

The order of execution requires you to know that there is a promise here so that the functions are called in this order:
1- fetch
2- bar
3- foo

Collapse
 
xuanchinhh97 profile image
Xuan Chinh

helpful

Collapse
 
dionarodrigues profile image
Diona Rodrigues

Thank you!

Collapse
 
jnareb profile image
Jakub Narębski

Can you get progress information and partial results with the fetch API?

Collapse
 
dionarodrigues profile image
Diona Rodrigues

Hey Jakub, I think I didn't get your questions. Can you explain it a little more?

Collapse
 
jnareb profile image
Jakub Narębski

With AJAX in onreadystatechange you can not only check whether you got full response, or an error, but also if you got partial response. It is useful for generating progress information if the response is slow (you receive results in spurts) - but of known final size, or for generating partial results.

Collapse
 
koas profile image
Koas • Edited

Hi Jakub, for progress and partial results Server Sent Events (SSE - developer.mozilla.org/es/docs/Web/...) may be what you’re looking for.

Collapse
 
mehuge profile image
Mehuge

Typo in the last example?

getUserData().catch(err => console.log(err))
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dionarodrigues profile image
Diona Rodrigues

Hey Mehuge, what do you mean by Typo?

 
dionarodrigues profile image
Diona Rodrigues

Hey @lukeshiru and @vetras, thanks for sharing your points. I think both ways are completely fine and I also think it depends on personal/team preferences.

Collapse
 
hesamdanaee profile image
Hesam

So helpful. Thx

Collapse
 
dionarodrigues profile image
Diona Rodrigues

Thank you!

Collapse
 
kosoko_daniel profile image
Oluwagbenga

Thank you

Collapse
 
dionarodrigues profile image
Diona Rodrigues

You're welcome!! :)

Collapse
 
nicholaskimuli profile image
Nicholas Kimuli

Thank you!

Collapse
 
dionarodrigues profile image
Diona Rodrigues

You're welcome!! :)

Collapse
 
andrewbaisden profile image
Andrew Baisden

Really good article with lots of info.

Collapse
 
dionarodrigues profile image
Diona Rodrigues

Thank you!

Collapse
 
jumashafara profile image
Kibekityo Juma Shafara

Very well organized, easy to understand thanks, am following you 😌

Collapse
 
dionarodrigues profile image
Diona Rodrigues

I appreciate it. Thank you! :)

Collapse
 
madrus profile image
Madrus

Thanks for putting it all together in one concept pointing out all the different possibilities. Very clear and to the point.

Collapse
 
dionarodrigues profile image
Diona Rodrigues

Thank you for your comment. I'm really glad you liked it.