async/await в JavaScript: преимущества и подводные камни

1
5988
Добавить в избранное

Конструкция async/await сделала большой вклад в асинхронное JS-программирование: можно использовать стиль синхронного кода для асинхронного.

Плюсы async/await

Существенным плюсом async/await является возможность использования синхронного стиля программирования. Пример:

Очевидно, что код с использованием данной конструкции гораздо проще чем тот, что создан с помощью promise. Если игнорировать await, программу не отличить от любого другого синхронного языка, например Python.

К плюсам можно отнести и простоту кода, и встроенную поддержку браузера. Сегодня практически все они умеют работать с асинхронными функциями.

async/await

Еще одним, но, пожалуй, менее очевидным преимуществом является async. Он объявляет, что функция getBooksByAuthor(), которая возвращает значение wait(), точно является promise. Поэтому можно безопасно вызывать getBooksByAuthorWithAwait().then.(..) или await getBooksByAuthorWithAwait().

В приведенном коде getBooksByAuthorWithPromise() может возвращать promise (обычный случай) или null (частный случай), но тогда нельзя будет безопасно вызвать .then().

async/await способен ввести в заблуждение

Иногда сравнение с promise сопровождается утверждением, будто это эволюция асинхронного JS-программирования, что довольно спорно. async/await – лишь “приправа” для вашего кода, его небольшой апдейт, и никак не изменит ваш стиль программирования полностью.

По сути, promise – это как раз те самые асинхронные функции. Чтобы их юзать, нужно сперва хорошо разобраться с promise. Его все же придется использовать чаще.

Рассмотрим функции getBooksByAuthorWithwait() и getBooksByAuthorWithPromises() в приведенном выше примере. Помимо функциональной идентичности, они также имеют одинаковый интерфейс. Таким образом, getBooksByAuthorWithAwait() вернет promise, если вызвать его напрямую.

Подводные камни в async/await

Какие ошибки можно допустить при использовании конструкции? Разберем наиболее характерные из них.

Слишком последовательно

Хоть await и может синхронизировать код, имейте в виду, что он по-прежнему является асинхронным. Поэтому необходимо позаботиться о том, чтобы он не был слишком последовательным.

Логически код кажется правильным, но вот вам опровержение:

  • await bookModel.fetchAll() ждет, пока не вернется fetchAll().
  • После этого вызывается await authorModel.fetch(authorId).

Хотим обратить ваше внимание на то, что authorModel.fetch(authorId) не зависит от bookModel.fetchAll(). Это означает, что их можно вызвать параллельно. Используя await, они станут последовательными, из-за чего время выполнения значительно возрастет.

Пример правильного кода:

Если вы хотите получить список элементов, необходимо полагаться на promise:

Грубо говоря, все равно нужно думать о рабочих процессах асинхронно, и только потом пытаться писать код синхронно с использованием await. В некоторых случаях использование promise будет более оправданным решением.

Обработка ошибок

С помощью promises асинхронная функция имеет два возможных возвращаемых значения: resolved value (разрешенное) и rejected value (отклоненное). Можно использовать .then() для обычного случая и .catch() для частного. Но, к сожалению, обработка ошибок async/await – довольно сложный процесс. Рассмотрим популярные примеры.

Try…catch

В основном применение блока try…catch влечет за собой принятие rejected value как resolved value. Пример:

Преимущества использования try…catch:

  • Простота. Если вы умеете работать с такими языками, как Java или C++, у вас не будет никаких трудностей.
  • Чтобы обработать ошибки в конкретной части программы, в 1 блок try…catch можно обернуть несколько await.

Существует также один недостаток в этом подходе. try…catch будет находить каждое исключение в блоке, которые обычно не определяется с помощью promise. Пример:

При запуске данного кода выведется ошибка: ReferenceError: cb не определен. Так происходит из-за того, что BookModel вызывается несколько раз, и один вызов проглатывает «Error».

Функция возвращает оба значения

Можно также обработать ошибки с помощью языка Go. Так, будут возвращены и результат, и ошибка. Пример использования функции:

.catch

Вспомним функционал await: он будет ждать promise, чтобы завершить работу. Также помним, что promise.catch() возвращает promise. Зная это, вполне можно написать такую обработку:

В этом подходе есть две незначительные проблемы:

  • Это смесь promise и асинхрон-функций.
  • Обработка ошибок происходит перед телом программы, что интуитивно не понятно.

Подведем итоги

Конструкция async/await неплохо улучшает асинхронное JS-программирование: анализ и отладка становятся проще. Но для корректного использования этих инструментов необходимо полностью понимать promise, т. к. он лежит в их основе

Оригинал

Другие материалы по теме:

Интересуетесь веб-разработкой?

Подпишитесь на нашу рассылку, чтобы получать больше интересных материалов:

И не беспокойтесь, мы тоже не любим спам. Отписаться можно в любое время.




1 комментарий

  1. День добрый.
    Не совсем понял эту конструкцию
    [err, user] = await to(UserModel.findById(1));
    Как такое выполнить в браузере?

Оставьте комментарий