Await Fetch

broken image


When does an asynchronous function finish? And why is this such a hard question to answer?

The response object, returned by the await fetch , is a generic placeholder for multiple data formats. For example, you can extract the JSON object from a fetch response: async function fetchMoviesJSON. Const response = await fetch ('/movies'); const movies = await response.json ; return movies. In this article, we will be learning and experimenting with Swift Async Await to fetch multiple REST API endpoints and eliminate Pyramid of Doom callback hell to improve code readability and maintanability. I will introduce Structured Concurrency Task API to execute single and parallel async tasks. Finally, we'll use Continuation API to interface current synchronous code with callback to async. GET request with async/await - Axios vs Fetch This sends the same GET request, but this version uses an async function and the await javascript expression to wait for the promises to return (instead of using the promise then method as above).

Well it turns out that understanding asynchronous functions requires a great deal of knowledge about how JavaScript works fundamentally.

Let's go explore this concept, and learn a lot about JavaScript in the process.

Are you ready? Let's go.

What is asynchronous code?

By design, JavaScript is a synchronous programming language. This means that when code is executed, JavaScript starts at the top of the file and runs through code line by line, until it is done.

The result of this design decision is that only one thing can happen at any one time.

You can think of this as if you were juggling six small balls. While you are juggling, your hands are occupied and can't handle anything else.

It's the same with JavaScript: once the code is running, it has its hands full with that code. We call this this kind of synchronous code blocking. Because it's effectively blocking other code from running.

Let's circle back to the juggling example. What would happen if you wanted to add another ball? Instead of six balls, you wanted to juggle seven balls. That's might be a problem.

You don't want to stop juggling, because it's just so much fun. But you can't go and get another ball either, because that would mean you'd have to stop.

Macx dvd ripper pro 4 6 4 download free. The solution? Delegate the work to a friend or family member. They aren't juggling, so they can go and get the ball for you, then toss it into your juggling at a time when your hand is free and you are ready to add another ball mid-juggle.

This is what asynchronous code is. JavaScript is delegating the work to something else, then going about it's own business. Then when it's ready, it will receive the results back from the work.

Who is doing the other work?

Alright, so we know that JavaScript is synchronous and lazy. It doesn't want to do all of the work itself, so it farms it out to something else.

But who is this mysterious entity that works for JavaScript? And how does it get hired to work for JavaScript?

Well, let's take a look at an example of asynchronous code.

Running this code results in the following output in the console:

Alright. What is going on?

It turns out that the way we farm out work in JavaScript is to use environment-specific functions and APIs. And this is a source of great confusion in JavaScript.

JavaScript always runs in an environment.

Often, that environment is the browser. But it can also be on the server with NodeJS. But what on earth is the difference?

The difference – and this is important – is that the browser and the server (NodeJS), functionality-wise, are not equivalent. They are often similar, but they are not the same.

Let's illustrate this with an example. Let's say JavaScript is the protagonist of an epic fantasy book. Just an ordinary farm kid.

Now let's say that this farm kid found two suits of special armor that gave them powers beyond their own.

When they used the browser suit of armor, they gained access to a certain set of capabilities.

When they used the server suit of armor they gained access to another set of capabilities.

These suits have some overlap, because the creators of these suits had the same needs in certain places, but not in others.

This is what an environment is. A place where code is run, where there exist tools that are built on top of the existing JavaScript language. They are not a part of the language, but the line is often blurred because we use these tools every day when we write code.

setTimeout, fetch, and DOM are all examples of Web APIs. (You can see the full list of Web APIs here.) They are tools that are built into the browser, and that are made available to us when our code is run.

And because we always run JavaScript in an environment, it seems like these are part of the language. But they are not.

So if you've ever wondered why you can use fetch in JavaScript when you run it in the browser (but need to install a package when you run it in NodeJS), this is why. Someone thought fetch was a good idea, and built it as a tool for the NodeJS environment.

Confusing? Yes!

But now we can finally understand what takes on the work from JavaScript, and how it gets hired.

It turns out that it is the environment that takes on the work, and the way to get the environment to do that work, is to use functionality that belongs to the environment. For example fetch or setTimeout in the browser environment.

What happens to the work?

Great. So the environment takes on the work. Then what?

At some point you need to get the results back. But let's think about how this would work.

Let's go back to the juggling example from the beginning. Imagine you asked for a new ball, and a friend just started throwing the ball at you when you weren't ready.

That would be a disaster. Maybe you could get lucky and catch it and get it into your routine effectively. But theres a large chance that it may cause you to drop all of your balls and crash your routine. Wouldn't it be better if you gave strict instructions on when to receive the ball?

As it turns out, there are strict rules surrounding when JavaScript can receive delegated work.

Those rules are governed by the event loop and involve the microtask and macrotask queue. Yes, I know. It's a lot. But bear with me.

Alright. So when we delegate asynchronous code to the browser, the browser takes and runs the code and takes on that workload. But there may be multiple tasks that are given to the browser, so we need to make sure that we can prioritise these tasks.

This is where the microtask queue and the macrotask queue come in play. The browser will take the work, do it, then place the result in one of the two queues based on the type of work it receives.

Promises, for example, are placed in the microtask queue and have a higher priority.

Events and setTimeout are examples of work that is put in the macrotask queue, and have a lower priority.

Now once the work is done, and is placed in one of the two queues, the event loop will run back and forth and check whether or not JavaScript is ready to receive the results.

Only when JavaScript is done running all its synchronous code, and is good and ready, will the event loop start picking from the queues and handing the functions back to JavaScript to run.

So let's take a look at an example:

What will the order be here?

  1. Firstly, setTimeout is delegated to the browser, which does the work and puts the resulting function in the macrotask queue.
  2. Secondly fetch is delegated to the browser, which takes the work. It retrieves the data from the endpoint and puts the resulting functions in the microtask queue.
  3. Javascript logs out 'What soup'?
  4. The event loop checks whether or not JavaScript is ready to receive the results from the queued work.
  5. When the console.log is done, JavaScript is ready. The event loop picks queued functions from the microtask queue, which has a higher priority, and gives them back to JavaScript to execute.
  6. After the microtask queue is empty, the setTimeout callback is taken out of the macrotask queue and given back to JavaScript to execute.

Promises

Now you should have a good deal of knowledge about how asynchronous code is handled by JavaScript and the browser environment. So let's talk about promises.

A promise is a JavaScript construct that represents a future unknown value. Conceptually, a promise is just JavaScript promising to return a value. It could be the result from an API call, or it could be an error object from a failed network request. You're guaranteed to get something.

A promise can have the following states:

  • fulfilled - action successfully completed
  • rejected - action failed
  • pending - neither action has been completed
  • settled - has been fulfilled or rejected

A promise receives a resolve and a reject function that can be called to trigger one of these states.

One of the big selling points of promises is that we can chain functions that we want to happen on success (resolve) or failure (reject):

  • To register a function to run on success we use .then
  • To register a function to run on failure we use .catch

Perfect. Now let's take a closer look at what this looks like under the hood, using fetch as an example:

So we can use promises to do asynchronous work, and to be sure that we can handle any result from those promises. That is the value proposition. If you want to know more about promises you can read more about them here and here.

When we use promises, we chain our functions onto the promise to handle the different scenarios.

This works, but we still need to handle our logic inside callbacks (nested functions) once we get our results back. What if we could use promises but write synchronous looking code? It turns out we can.

Async/Await

Async/Await is a way of writing promises that allows us to write asynchronous code in a synchronous way. Let's have a look.

Nothing has changed under the hood here. We are still using promises to fetch data, but now it looks synchronous, and we no longer have .then and .catch blocks.

Async / Await is actually just syntactic sugar providing a way to create code that is easier to reason about, without changing the underlying dynamic.

Let's take a look at how it works.

Async/Await lets us use generators to pause the execution of a function. When we are using async / await we are not blocking because the function is yielding the control back over to the main program.

Then when the promise resolves we are using the generator to yield control back to the asynchronous function with the value from the resolved promise.

You can read more here for a great overview of generators and asynchronous code.

In effect, we can now write asynchronous code that looks like synchronous code. Which means that it is easier to reason about, and we can use synchronous tools for error handling such as try / catch:

Alright. So how do we use it? In order to use async / await we need to prepend the function with async. This does not make it an asynchronous function, it merely allows us to use await inside of it.

Failing to provide the async keyword will result in a syntax error when trying to use await inside a regular function.

Because of this, we can not use async / await on top level code. But async and await are still just syntactic sugar over promises. So we can handle top level cases with promise chaining:

This exposes another interesting fact about async / await. When defining a function as async, it will always return a promise.

Using async / await can seem like magic at first. But like any magic, it's just sufficiently advanced technology that has evolved over the years. Hopefully now you have a solid grasp of the fundamentals, and can use async / await with confidence.

Await fetch error

If you made it here, congrats. You just added a key piece of knowledge about JavaScript and how it works with its environments to your toolbox.

This is definitely a confusing subject, and the lines are not always clear. But now you hopefully have a grasp on how JavaScript works with asynchronous code in the browser, and a stronger grasp over both promises and async / await.

If you enjoyed this article, you might also enjoy my youtube channel. I currently have a web fundamentals series going where I go through HTTP, building web servers from scratch and more.

There's also a series going on building an entire app with React, if that is your jam. And I plan to add much more content here in the future going in depth on JavaScript topics.

And if you want to say hi or chat about web development, you could always reach out to me on twitter at @foseberg. Thanks for reading!

The Fetch API is the default tool to make network in web applications. While fetch() is generally easy to use, there some nuances to be aware of.

In this post, you'll find the common scenarios of how to use fetch() with async/await syntax. You'll understand how to fetch data, handle fetch errors, cancel a fetch request, and more.

Table of Contents

1. Intro to fetch()

The Fetch API accesses resources across the network. You can make HTTP requests (using GET, POST and other methods), download, and upload files.

To start a request, call the special function fetch():

which accepts 2 arguments:

  • resource: the URL string, or a Request object
  • options: the configuration object with properties like method, headers, body, credentials, and more.

fetch() starts a request and returns a promise. When the request completes, the promise is resolved with the Response object. If the request fails due to some network problems, the promise is rejected.

async/await syntax fits great with fetch() because it simplifies the work with promises.

For example, let's make a request to fetch some movies:

fetchMovies() is an asynchronous function since it's marked with the async keyword.

await fetch('/movies') starts an HTTP request to '/movies' URL. Because the await keyword is present, the asynchronous function is paused until the request completes.

When the request completes, response is assigned with the response object of the request. Let's see in the next section how to extract useful data, like JSON or plain text, from the response.

2. Fetching JSON

The response object, returned by the await fetch(), is a generic placeholder for multiple data formats.

For example, you can extract the JSON object from a fetch response:

response.json() is a method on the Response object that lets you extract a JSON object from the response. The method returns a promise, so you have to wait for the JSON: await response.json().

Exhibeo 1 1 4 – create beautiful html5 presentations. The Response object offers a lot of useful methods (all returning promises):

  • response.json() returns a promise resolved to a JSON object
  • response.text() returns a promise resolved to raw text
  • response.formData() returns a promise resolved to FormData
  • response.blob() returns a promise resolved to a Blob (a file-like object of raw data)
  • response.arrayBuffer()() returns a promise resolved to an ArryBuffer (raw generic binary data)

3. Handling fetch errors

When I was familiarizing with fetch(), I was surprised that fetch() doesn't throw an error when the server returns a bad HTTP status, e.g. client (400–499) or server errors (500–599).

Await Fetch Api

For example, let's access a non-existing page '/oops' on the server. As expected, such request ends in a 404 response status:

When fetching the URL '/oops' the server responds with status 404 and text 'Page not found'. Surprisingly, fetch() doesn't throw an error for a missing URL, but considers this as a completed HTTP request.

fetch() rejects only if a request cannot be made or a response cannot be retrieved. It might happen because of network problems: no internet connection, host not found, the server is not responding.

Fortunately, response.ok property lets you separate good from bad HTTP response statuses. The property is set to true only if the response has status 200-299.

In the above example, the response.ok property is false because the response has the status 404.

If you'd like to throw an error on a bad HTTP status (outside of the range 200-299), check the value of response.ok property and throw an error manually:

Await Fetch Js

4. Canceling a fetch request

Unfortunately, fetch() API alone doesn't allow to cancel a fetch request once started. To cancel a fetch request you need an additional tool AbortController.

Connecting fetch() and AbortController requires 3 steps:

A) Before starting the request, create an abort controller instance: controller = new AbortController().

B) When starting the request properly, use the options argument of fetch(url, { signal: controller.signal }) and set signal property to be controller.signal.

C) Finally, if you need to cancel the request, just call controller.abort() method.

For example, let's implement 2 buttons that control a fetch request. Clicking the button Fetch movies starts a fetch() request, while clicking Cancel fetch aborts the request in progress:

Open the demo. Click Fetch movies to start the request, then right away click Cancel fetch to cancel it. This makes the active request cancel: await fetch() gets rejected by throwing an abort error. The catch block then catches the abort error.

The abort controller instances aren't reusable. Each time you start a fetch() request, you have to create a new abort controller instance for each request.

On a side note, if you'd like to timeout a fetch() request, follow my post How to Timeout a fetch() Request.

Await Fetch

5. Parallel fetch requests

To perform parallel fetch requests use the Promise.all() helper function.

Let's start 2 parallel requests to fetch movies and categories:

await Promise.all([..]) starts fetch requests in parallel, and waits until all of them are resolved.

If any request fails, then the whole parallel promise gets rejected right away with the failed request error.

In case if you want all parallel requests to complete, despite any of them fail, consider using Promise.allSettled().

Js Await Fetch

6. Summary

Calling fetch() starts a request and returns a promise. When the request completes, the promise resolves to the response object. From the response object you can extract data in the format you need: JSON, raw text, Blob.

Await Fetch Url

Because fetch() returns a promise, you can simplify the code by using the async/await syntax: response = await fetch().

You've found out how to use fetch() accompanied with async/await to fetch JSON data, handle fetching errors, cancel a request, perform parallel requests.

Having mastered the basics of fetch() with async/await, follow my post on How to Timeout a fetch() Request.

Still have questions on how to use fetch()? Write a question in the comments below!

Like the post? Please share!

Quality posts into your inbox

I regularly publish posts containing:

  • Important JavaScript concepts explained in simple words
  • Overview of new JavaScript features
  • How to use TypeScript and typing
  • Software design and good coding practices

Subscribe to my newsletter to get them right into your inbox.

About Dmitri Pavlutin

Software developer, tech writer and coach. My daily routine consists of (but not limited to) drinking coffee, coding, writing, coaching, overcoming boredom 😉.

Recommended reading:





broken image