# Асинхронные функции

## 1. Введение <a href="#vvedenie" id="vvedenie"></a>

Работа с бекендом может быть довольно сложной, после одной асинхронной операции необходимо сделать еще один запрос на сервер по полученным данным и так несколько раз.

Представьте следующую ситуацию:

* Нам необходимо запросить профиль пользователя от `/user-profile`
* После того как мы получили профиль, нам необходимо запросить всех друзей этого пользователя от `/users/:userId/friends`
* Мы можем запрашивать данные только одного из друзей за одно обращение к серверу, допустим `/users/:userId`
* И только после того как мы запросили всех друзей, мы можем отобразить это пользователю.

Реализация такого алгоритма псевдокодом на промисах может выглядеть следующим образом:

```js
fetch('/user-profile')
  .then(user => fetch(`/users/${user.id}/friends`))
  .then(idList => {
    const friends = idList.map(id => fetch(`/users/${id}`));
    return Promise.all(friends);
  })
  .then(friends => console.log(friends))
  .catch(error => console.error(error));
Copy
```

Согласитесь, не самый легкочитаемый код, при том, что операции очень простые. Внутри `then` мы передаем функции-обработчики и в результате получаются елки вложенности. Вот бы можно было сделать так, что наш код выглядел следующим образом.

```js
const promise = fetch('/users');
/*
 * Как-то заставить JS подождать пока выполнится промис
 * После чего просто использовать данные, без then и т. д.
 */
console.log(promise.result);
Copy
```

Но проблема в том, что JavaScript не многопоточный язык, поэтому любое ожидание в коде блокирует весь скрипт. Например когда мы используем `alert` и все виснет до момента выхода из него. На помощь приходят генераторы.

## 2. Асинхронные функции <a href="#asinkhronnye-funkcii" id="asinkhronnye-funkcii"></a>

**Асинхронные функции (async/await)** - прослойка над промисами и генераторами, которая абстрагирует сложности их использования и предоставляет удобный интерфейс.

Для создания асинхронной функции перед `function` добавляем ключевое слово `async`. После чего, внутри такой функции мы можем использовать оператор `await` и справа от `await` поставить что-то, что вернет промис. Всегда в таком порядке `await -> promise`.

```js
const getUsers = async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/users');
  const users = response.json();

  return users;
};

getUsers().then(users => console.log(users));
Copy
```

При такой записи, `await` приостановит только эту функцию (не весь скрипт) и будет ждать, пока не выполнится промис. Как только промис выполнился - исполнение функции возобновляется и на строке ниже нам доступен результат асинхронной операции.

* Асинхронная функция всегда возвращает промис. Даже если написать `return 'hello'`, строка будет обернута в промис, который резолвится с этой строкой.
* Оператор `await` приостанавливает функцию до того момента, когда промис выполнился, то есть перешел в состояние `Settled`
* Оператор `await` или вернет значение успешного(`Fullfilled`) промиса, или выбросит ошибку в случае если промис был отклонен(`Rejected`)
* Оператор `await` можно использовать только в теле асинхронной функции.
* Можно использовать любые методы промисов внутри асинхронной функции, к примеру `Promise.all`

Для того чтобы обработать ошибку используется конструкция `try/catch`- аналог блока `catch`.

```js
const getUsers = async () => {
  try {
    const result = await fetch('https://jsonplaceholder.typicode.com/users');
    console.log(result);
  } catch (err) {
    throw err;
  }
};

getUsers()
  .then(users => console.log(users))
  .catch(error => console.log(error));
Copy
```

* Не забывайте ставить `await` перед промисом
* Не используйте `await` в методах вроде `map`, `filter` и т. д., работает не так как можно ожидать
* Хотя код выглядит синхронным, не забывайте, что исполнение кода в такой функции приостанавливается и возобновляется с течением времени.

Теперь перепишем наш код, используя асинхронную функцию.

```js
const getUserFriends = async () => {
  const user = await fetch('/user-profile');
  const idList = await fetch(`/users/${user.id}/friends`);
  const promises = idList.map(id => fetch(`/users/${id}`));
  const friends = await Promise.all(promises);

  return friends;
};

// Асинхронная функция всегда вернет промис
const promise = getUserFriends();
promise.then(friends => console.log(friends));
Copy
```

## 3. Дополнительные материалы <a href="#dopolnitelnye-materialy" id="dopolnitelnye-materialy"></a>

* [Async/await: 6 причин забыть о проми\`сах](https://habrahabr.ru/company/ruvds/blog/326074/)
* [Fireship - The Async Await](https://youtu.be/vn3tm0quoqE)
* [Всё, что нужно знать об async/await](https://medium.com/@stasonmars/%D0%B2%D1%81%D0%B5%CC%88-%D1%87%D1%82%D0%BE-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D0%B7%D0%BD%D0%B0%D1%82%D1%8C-%D0%BE%D0%B1-async-await-%D1%86%D0%B8%D0%BA%D0%BB%D1%8B-%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D1%8C-%D0%BF%D0%BE%D1%82%D0%BE%D0%BA%D0%BE%D0%B2-%D0%BE%D0%B3%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D0%BD%D0%B8%D1%8F-76dde2cb6949)
* [Understanding JavaScript’s async await](https://ponyfoo.com/articles/understanding-javascript-async-await)
