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

Go внешний интерфейс Безопасность JavaScript
JavaScript async/await: преимущества, недостатки и правильное использование

Оригинальный адрес:JavaScript async/await: The Good Part, Pitfalls and How to Use

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

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

async/awaitНаиболее важным преимуществом, которое он нам дает, является синхронный стиль программирования. Давайте посмотрим на пример.

Это понятно,async/awaitсоотношение версийpromiseВерсии легче понять. если игнорироватьawaitключевое слово, код выглядит точно так же, как любой другой синхронный язык, такой как Python.

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

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

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

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

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

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

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

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

Если вы позвоните напрямуюgetBooksByAuthorWithAwait(), что означает, что он вернетpromise.

Что ж, это неплохо. ТолькоawaitНазвание звучит как «о, это может превратить асинхронную функцию в функцию синхронизации», что на самом деле неверно.

Яма Async/await

затем используйтеasync/awaitкакая ошибка возникает? Вот несколько распространенных ситуаций.

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

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

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

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

Это правильный путь:

Или, что еще хуже, если вы хотите получить элементы списка один за другим, вам придется полагаться наpromise:
Проще говоря, вам все равно нужно думать о рабочем процессе асинхронно, а затем пытаться писать код синхронно.await. В сложных рабочих процессах используйте напрямуюpromisesМожет быть проще.

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

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

Попробуйте поймать

Самый стандартный (и то, что я рекомендую) способ - использоватьtry...catchутверждение. когдаawaitПри вызове любое отклоненное значение будет выброшено как исключение. Вот пример:

catchОшибка - это именно отклоненное значение. После того, как мы нашли исключение, есть несколько способов его обработки:

  1. Обрабатывать исключения и возвращать нормальные значения. (не вcatchзаблокировать с помощью любогоreturnоператор, который эквивалентен использованиюreturn undefined, также является нормальным значением. )
  2. Бросьте его, если вы хотите, чтобы вызывающий абонент обработал его. Может генерировать обычный объект ошибки напрямуюthrow error, который вpromiseразрешено в цепочкеasync getBooksByAuthorWithAwait()функция (т.е. ее все еще можно вызывать такgetBooksByAuthorWithAwait().then(...).catch(error => ...)); или вы можете использоватьErrorОшибки переноса объектов, например.throw new Error(error), предоставит полную трассировку стека, когда эта ошибка отображается в консоли.
  3. отказаться от него, какreturn Promise.reject(error). Это эквивалентноthrow errorНе рекомендуется.

Преимущество использования try...catch заключается в том,:

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

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

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

заставить функцию возвращать два значения

Другой способ обработки ошибок вдохновлен языком Go. Это позволяет асинхронным функциям возвращать ошибки и результаты. Подробнее см. в этом сообщении в блоге:How to write async await without try-catch blocks in Javascript

Короче говоря, вы можете использовать что-то вроде этогоawaitфункция:

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

использовать .поймать

Последний метод, который мы здесь рассмотрим, — это использовать.catch().

передуматьawaitФункция: он будет ждатьpromise完成其工作。 Отзывать,promise.catch()также будетpromise, поэтому мы можем написать обработку ошибок следующим образом:

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

  1. этоpromisesа такжеawaitСмесь функций. тебе еще нужно знатьpromisesПринцип работы.
  2. Обработка ошибок происходит до нормального пути, который не интуитивно понятен.

В заключение

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