JavaScript async / await: Преимущества, возможные сложности и способы использования

JavaScript async / await: Преимущества, возможные сложности и способы использования

От автора: Async await JavaScript, представленный в ES7, является фантастическим улучшением асинхронного программирования. Он предоставил возможность использовать код в синхронном стиле для доступа к ресурсам асинхронно, без блокировки основного потока. Однако этот способ немного сложен в использовании. В этой статье мы рассмотрим async / wait с разных точек зрения и покажем, как использовать их правильно и эффективно.

Преимущества async / wait

Важнейшим преимуществом async / wait является синхронный стиль программирования. Давайте рассмотрим пример.

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

Современные тенденции и подходы в веб-разработке

Узнайте алгоритм быстрого профессионального роста с нуля в сайтостроении

Узнать подробнее

И лучшее — это не только лучше читаемо, async / await имеет встроенную поддержку браузеров. На сегодняшний день все основные браузеры полностью поддерживают асинхронные функции.

Все основные браузеры поддерживают асинхронные функции

Встроенная поддержка означает, что вам не нужно транспилировать код. Что еще более важно, это облегчает отладку. Когда вы устанавливаете контрольную точку в точке входа функции и переходите через строку await, вы увидите прерывание отладчика, в то время как bookModel.fetchAll() выполняет свою работу, а затем переходит к следующей строке .filter! Это намного проще, чем с promise, для которого вам нужно настроить другую контрольную точку в строке .filter.

Отладка асинхронной функции. Отладчик будет ожидать строки await и перейдет дальше после обработки

Еще одним очевидным преимуществом является ключевое слово async. Оно объявляет, что возвращаемое значение функции getBooksByAuthorWithAwait() гарантировано является promise, так что вызывающие объекты могут безопасно вызывать getBooksByAuthorWithAwait().then(…) или await getBooksByAuthorWithAwait(). Рассмотрим следующий случай (плохая практика!):

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

Async / wait могут ввести в заблуждение

Некоторые статьи сравнивают async / await с Promise и утверждают, что это следующее поколение в эволюции асинхронного программирования JavaScript, с чем я — при всем уважении — не согласен. Async / await — это улучшение, но это не более чем синтаксический прием, который полностью не изменит наш стиль программирования.

По сути, асинхронные функции по-прежнему являются Promise. Вы должны понимать Promise, прежде чем сможете использовать асинхронные функции правильно, и что еще хуже, большую часть времени вы должны использовать Promise вместе с асинхронными функциями.

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

Это означает, что getBooksByAuthorWithAwait() вернет promise, если вы его вызовете напрямую. Ну, это не обязательно плохо. Только слово await дает людям ощущение: «О, отлично, это может преобразовать асинхронные функции в синхронные», что на самом деле неверно.

Сложности с await / await

Итак, какие ошибки могут быть допущены при использовании async / wait? Вот некоторые из них.

Не будьте слишком последовательными

Хотя wait может сделать ваш код выглядящим синхронным, имейте в виду, что он все еще асинхронный, и нужно быть осторожными, чтобы не быть слишком последовательными.

Этот код выглядит логически корректным. Однако это неверно.

await bookModel.fetchAll() будет ждать, пока не будет возвращена fetchAll().

После этого будет вызвана await authorModel.fetch(authorId).

Обратите внимание, что authorModel.fetch(authorId) не зависит от результата bookModel.fetchAll(), и на самом деле их можно вызывать параллельно! Однако, используя await мы делаем эти два вызова последовательными, и общее время выполнения будет намного больше, чем при параллельном выполнении. Вот корректный способ:

Или еще хуже, если вы хотите получить список элементов один за другим, вы должны полагаться на promises:

Современные тенденции и подходы в веб-разработке

Узнайте алгоритм быстрого профессионального роста с нуля в сайтостроении

Узнать подробнее

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

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

При использовании promises асинхронная функция может возвращать два значения: обработанное значение и отклоненное значение. И мы можем использовать .then() для стандартного случая и .catch() для исключения. Однако при использовании async/await обработка ошибок может быть более сложной.

try…catch

Стандартный (и рекомендуемый мой) способ — использовать инструкцию try … catch. При вызове await любое отклоненное значение будет введено как исключение. Вот пример:

Перехваченная ошибка — это собственно отклоненное значение. После того, как мы перехватили исключение, у нас есть несколько способов обработать его:

Обработать исключение и вернуть нормальное значение. (Неиспользование оператора return в блоке catch эквивалентно использованию return undefined и также является нормальным значением.)

Ввести его, если вы хотите, чтобы вызывающий объект обработал его. Вы можете либо напрямую ввести простой объект ошибки, например, throw error;, который позволяет использовать функцию async getBooksByAuthorWithAwait() в цепочке promises (т. е. вы все равно можете называть ее как getBooksByAuthorWithAwait().then(…).catch(error => …)); или вы можете обернуть ошибку в объект Error, например, throw new Error(error), которая даст полную трассировку стека, когда эта ошибка будет отображаться в консоли.

Отклонить его, например, return Promise.reject(error). Это эквивалентно throw error, поэтому не рекомендуется.

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

Простой, традиционный способ. Если у вас есть опыт работы с другими языками, такими как Java или C++, вам не составит труда разобраться в нем.

Вы можете обернуть несколько вызовов в await один блок try … catch для обработки ошибок в одном месте, если обработка ошибок на каждом шаге не требуется.

У этого подхода есть и один недостаток. Так как try … catch перехватывает каждое исключение в блоке, будут перехвачены некоторые исключения, которые обычно не перехватываются для promises. Рассмотрим следующий пример:

Запустите этот код и вы получите ошибку ReferenceError: cb is not defined в консоли, черного цвета. Ошибка выводилась с помощью console.log(), но не самим JavaScript. Иногда это может быть критически: если BookModel заключен глубоко в ряд вызовов функций, и один из вызовов выдает ошибку, будет очень сложно найти ошибку, подобную этой.

Возврат функциями обоих значений

Другой способ обработки ошибок — это язык Go. Он позволяет асинхронным функциям возвращать как ошибку, так и результат. Короче говоря, вы можете использовать асинхронную функцию следующим образом:

Лично мне не нравится этот подход, поскольку он привносит в JavaScript стиль Go, который кажется неестественным, но в некоторых случаях это может оказаться весьма полезным.

Использование .catch

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

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

Это смесь promises и асинхронных функций. Вы все равно должны понимать, как работают promises, чтобы читать его.

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

Заключение

Ключевые слова async/await, введенные ES7, безусловно, являются улучшением асинхронного программирования JavaScript. Это может сделать код более простым для чтения и отладки. Однако, чтобы правильно использовать их, нужно полностью понимать promises, так как они не более чем синтаксический прием, а основной метод по-прежнему promises.

Надеюсь, что этот пост помог вам получить лучшее представление о том, что такое async/await, и это поможет вам не допускать некоторые распространенные ошибки. Спасибо за прочтение, и, пожалуйста, похлопайте мне, если вам понравился этот пост.

Автор: Charlee Li

Источник: https://hackernoon.com/

Редакция: Команда webformyself.

Современные тенденции и подходы в веб-разработке

Узнайте алгоритм быстрого профессионального роста с нуля в сайтостроении

Узнать подробнее

Курс по JavaScript: основы

Изучите JavaScript с нуля до результата!

Смотреть курс

Метки:

Похожие статьи:

Комментарии Вконтакте:

Комментарии Facebook:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Я не робот.

Spam Protection by WP-SpamFree