Asynchronous Programming: Using Fetch and Then
Published: 2023-12-20 22:23:41

When building a webpage, we almost always want to access information found in other places on the internet. Not only does it create a more interesting and engaging application, it also prevents us from having to have all of the information needed for our site on a single server. In order to access data remotely with JavaScript, we use the [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/fetch) function.


## Fetch

The `fetch()` function is a global method implemented by **Window** and **WorkerGlobalScope**. Because of this, `fetch()` is available to use in virtually every context in which you might need it. It takes time to access information online, however. Generally speaking, we usually don't want our code to have to wait for a single fetch request to finish before moving on to other steps. Luckily for us, `fetch()` is an asynchronous function, meaning it returns a **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)** instance while the next lines of code can run. 


## Promises

A **Promise** instance is exactly what it sounds like; it's a promise that the data will be passed to the code as soon as the information from the `fetch()` request is received.

That leaves us with another problem, however. If the rest of the code runs while the **Promise** is waiting to resolve, then the information received by `fetch()` will be left with no code to handle it. To solve this issue, there are a couple of tools we can use: [`await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) and [`then()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then). 


## Await and Then

`Await` is an operator that is useful when attempting to assign a Promise instance to a variable, while `then()` is used to immediately call a callback function when the **Promise** resolves. For this blog, we're going to be looking at how `fetch()` and `then()` interact with each other and leave `await` for another time.


## Using Fetch

Building on what we read earlier, the `fetch()` function can take a couple arguments but only needs one. For simplicity's sake, we will only be looking at the required argument, but keep in mind there are other options that can be added to manipulate the functionality.

The first and main argument passed to the `fetch()` function is the resource, or the URL of the information that you are trying to access.

```
fetch('http://localhost:3000/data')
```
 
As stated earlier, `fetch()` will attempt to access the data, and while doing so, will return a **Promise** instance that will be replaced with the real information at a later time. We call this resolved request the **Response** object.


##Using Then

Once we have a **Promise** instance, it can then be passed to the `then()` function to be used. `then()` is a method used by **Promise** instances that has one mandatory argument and an optional one: a callback function to be used on successful resolutions and/or one for rejected resolutions of the **Promise**. We will start by only looking at instances of `then()` using only one argument.

Combined with a fetch request, it looks something like this:

```
fetch('http://localhost:3000/data')
.then(res => res.json())
```

In the example above, we took the response from the `fetch()` request and passed it to an arrow function. This callback function changes the **JSON** string that is the response and returns a usable object for later use. Remember that this response is only translated once the **Promise** passed to the `then()` function resolves.

From here, we can link together at many `then()` functions as we desire. This is because each `then()` function immediately returns a **Promise** instance. 

```
fetch(`http://localhost:3000/data`)
.then(res => res.json())
.then(response => someFunction(response))
.then(someValue => console.log(someValue))
```

As each **Promise** is fulfilled, the callback functions will be called in the order that they are chained.

As mentioned earlier, we can add another call back function as an argument to a `then()` function to be used in case our original **Promise** does not resolve. In that case, we would have something like this:

```
fetch(`http://localhost:3000/data`)
.then(res => res.json(), () => console.log("fetch unsuccessful"))
```


## Resolving Promises

A `then()` function will choose either of the callback functions depending on how the **Promise** passed to it resolves. These are the ways **Promises** can be handled:

•If a value is returned, the **Promise** is fulfilled with the returned value as the value of the **Promise**.
•If no value is returned, the **Promise** is fulfilled with undefined as the value.
•If an error is thrown, the **Promise** is rejected with the error being its value.
•If a fulfilled **Promise** is returned, the **Promise** is fulfilled with the returned value as its own value.
•If a rejected **Promise** is returned, the **Promise** is rejected and shares the returned value.
•If another pending Promise is returned, the **Promise** remains pending and becomes fulfilled as soon as the previous **Promise** resolves.
	
There are various different ways to alter the behavior of the `fetch()` and `then()` methods beyond what has been discussed in this blog. For example, we can add more arguments to `fetch()` such as a method, headers, body, etc. However, for simplistic use, this blog covers most of what you need to know. Thank you for reading!