Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(content): add async await js article #109

Merged
merged 1 commit into from
Oct 3, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions src/content/resources/javascript/promises/async-await.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
---
authors:
- "ddivad#1337"
created_at: "2019/10/01"
title: Async Await
recommended_reading:
- javascript/promises/intro
---

## Async Await

Async / Await is another way to handle the calling of asynchronous code and is built on top of Promises. It is a nice and clean way to handle asynchronous code, without the need to `.then()` functions to the promise.

It is made up of 2 keywords as the name suggests: `async` and `await`. These need to be used together and can for this method to work.

## Async

The `async` keyword is used to show that the function is going to return a promise. Any return values from the function will be converted to a promise automatically, if they are not already.

```js
async function foo() {
return "foo";
}
```

This basic example will return a promise with the value of `foo`.

## Await

The `await` keyword is used to wait until a promise has executed and fetches the result. In order to use the `await` keyword, you **need** to be inside an `async` function.

```js
async function demo() {
let data = await foo();
console.log(foo); // "foo"
}
```

This code will call the `foo()` function from above and will wait on that line until the promise returned from the `async` function has returned.

Another example to demonstrate this is if we extended the `foo()` function from above to add a 5 second delay.

```js
async function foo() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("foo"), 5000)
});

let data = await promise; // Execution waits here until promise resolves
console.log(data); // "foo"
}
```

## Error Handling

As with Promises, if the Promise returns with an error, it will affect the `await` call that triggered it. When using Promises without Async / Await, the `.catch()` syntax is used, and this also exists with Aysnc / Await using the `try...catch` syntax. This can be demonstrated by making our `foo()` function throw an error.

```js
async function foo() {
throw new Error("An Error Occurred");
}
```

This would be handled using Aysnc / Await like so:

```js
async function getData() {
try {
let data = await foo();
console.log(data);
} catch (err) {
console.error(err); // err: An Error Occurred
}
}
```

## Beginner Mistakes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section should absolutely be mentioning the mistake of wrapping promises inside new Promise, that's something I've seen too many times.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although we might just want to edit the original promise article for that instead, I guess I could see that going either way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point - I guess adding in to the original promise article might be better here, as I have that article listed in the recommended reading section, and can add that one there explicitly.

People should read that one first, so the way I was approaching this section here was like an addition to the common mistakes from the original promise article, rather than adding them in both places.


As mentioned in the [intro](./intro.md), Promises are the highest source of confusion for beginners. Async / Await adds another layer on top of Promises, and comes with its own pitfalls.

`await` will **only** work if the function you try to add `await` to is `async`. Using `await` in top level code will not work (yet).

```js
let data = await foo(); // syntax error
```

To get around this issue, you can use an declare asynchronous function anonymously, if needed:

```js
(async () => {
let data = await foo();
console.log(data); // "foo"
})();
Comment on lines +90 to +93
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a good idea to to add a single link to the IIFE you're using here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean here? Do you mean add a link to what an IIFE is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, either that or just turn it into a regular function call. I think we probably want to keep the examples as simple as possible in every way

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I get what you're saying, but to me an IIFE here makes sense as people can copy / paste the code and it will just work. If it was a normal function instead, they would have to make an async function themselves if they wanted to run the code and check the output, which was the reason I used it here. I'll see if I can find a good link to put in here maybe that can explain them, or maybe even write an explanation in some other article here for how they work.

```

Top-Level-Await is something that *may* get added to Javascript in the future, but for now wrapper functions like above are needed for this functionality.

## Real World Example

Here is a real function that gets character information from the Rick and Morty API.
You can try it in your browser if you want to test it out.

```js
async function getCharacters() {
const response = await fetch(`https://rickandmortyapi.com/api/character`);
const data = await response.json();
return data.results;
}

(async () => {
try {
let characters = await getCharacters();
console.log(characters) // (20) [{...}, {...}, {...}]
} catch (err) {
console.error(err);
}
})();
```

In this example, we are querying real data from the Rick & Morty API. This API has a lot of information regarding Rick & Morty, but here, we are trying to retrieve all the characters.
- The first thing we do is use `fetch` to retrieve the data from the API. `fetch` is a built in function in web browsers to do http requests, and returns a Promise by default. We `await` the result of this.
- When we get the result, we need to get the `JSON` value of the data. To do this, the `.json()` method from fetch is used. We then return the results.
- As `await` can only be used in an `async` function, we use the method from above to make this work. As `getCharacters()` returns a Promise due to it being `async`, we `await` the result. We surround this in a `try...catch` in case fetch returns an error. Then, if no error is returned, `characters` contains the information we want, and is logged to the console. If `getCharacters()` returns an error, that error is also logged.

## Comparision with default Promises:

Below is the same code implemented without using Async / Await as a comparision. Here, you can see the differences between the two methods, and how Aysnc / Await makes the code appear in a *more synchronous* pattern, and can be clearer to follow.
```js
function getCharacters() {
return fetch(`https://rickandmortyapi.com/api/character`)
.then(response => response.json())
.then(response => response.results)
}

getCharacters().then(characters => {
console.log(characters) // (20) [{...}, {...}, {...}]
}).catch(err => console.error(err))
```