Сделайте свой код чище и читабельнее с помощью лучшей функции ES8

Async-await может быть одним из величайших недавних дополнений к JavaScript. Это не только упрощает работу программиста, но и делает код чистым и гладким.

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

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

В этой статье мы узнаем о двух новых функциях, которые появились с выпуском ES8, то есть async-await.

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

JavaScript как синхронный язык

JavaScript - это синхронный и однопоточный язык. Асинхронное поведение не является непосредственно частью JavaScript. Скорее, он может быть построен поверх самого основного языка JavaScript, и к нему можно получить доступ через API-интерфейсы браузера.

Что такое цикл событий и как он работает?

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

Мы используем стек вызовов, потому что иногда даже механизму JavaScript сложно отслеживать выполнение всей программы; он полагается на стек вызовов для предоставления ценной информации.

Стек вызовов очень похож на обычный стек с теми же операциями, а именно push и pop . Структура данных стека соответствует концепции LIFO. (Последним пришел - первым ушел), а под нажатием , мы подразумеваем помещение чего-либо в стек.

Когда функция сначала выполняет / вызывает себя, движок JavaScript помещает эту функцию в стек вызовов. Каждый вызов функции последовательно помещается в стек вызовов.

Точно так же pop означает удаление чего-либо из стека. Когда функция на самом верху стека завершает выполнение, она удаляется из стека вызовов, как это следует за свойством LIFO.

Как работают стеки вызовов

Теперь, когда мы знаем, за какими свойствами и концепциями следует стек вызовов, давайте посмотрим, как он работает на практике. Взгляните на этот фрагмент кода:

В этом коде у нас есть три оператора console.log() внутри функции main(), которая просто регистрирует строковое значение в терминале.

Два из этих утверждений представлены справа вверху и внизу, во второй и шестой строках соответственно. Другой оператор находится между ними, заключая себя в setTimeout call (с использованием API браузера, о котором мы поговорим позже) с временем ожидания, равным нулю миллисекунды.

Как работает цикл событий

  • Когда программа запускается, она входит в основную функцию. В результате main() сначала помещается в стек вызовов как кадр. После входа в основную функцию первым выполняется оператор console.log(“First”). Этот оператор также помещается в стек браузером. После выполнения он выталкивает фрейм и отображает First в консоли.
  • После выполнения первого оператора следующий оператор помещается в стек (setTimeout). Этот оператор использует API браузера для задержки времени выполнения функции обратного вызова, присутствующей внутри. Этот фрейм выскакивает из стека после передачи функции middle() браузеру для выполнения. Это потому, что это API браузера, и он выполняется в определенное время в ноль миллисекунд.
  • После выполнения третий оператор, или console.log(“Third”), помещается в стек вызовов для выполнения. Это происходит, когда таймер работает в фоновом режиме для выполнения функции middle(). Как только браузер помещает этот кадр в стек вызовов, он выполняется и отображает “Third” в консоли, выдавая последний кадр.
  • Функция setTimeout получает нулевую миллисекундную задержку, в результате чего обратный вызов добавляется в очередь событий, как только браузер получает его, поскольку время выполнения уже истекло.
  • Как только последний оператор выполняется и выходит из стека вызовов, кадр main() также выскакивает, делая его пустым. Чтобы браузер мог отправить любое сообщение из очереди событий в стек вызовов, он должен быть полностью пустым. Вот почему, хотя задержка составляла ноль миллисекунд, обратный вызов все равно должен ждать.
  • Наконец, оператор console.log(“Second”) внутри обратного вызова middle() помещается в стек вызовов и выполняется, таким образом выводя “Second” в консоль.

Это цикл событий JavaScript:

Что такое обещания?

Обещания в JavaScript точно такие же, как обещания в реальной жизни.

Представьте, что вы пообещали другу купить ему его любимую книгу. Чтобы выполнить это обещание, вам необходимо:

  • Зайдите в любой книжный магазин, где вы могли бы найти книгу.
  • Купи это и принеси ему

Пока это происходит, все, что делает ваш друг, ждет вас.

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

Точно так же в JavaScript обещание - это объект, который выдает значение когда-нибудь в будущем. Он может вернуть успешный результат или дать нам результат ошибки, но он не остановит выполнение программы на полпути.

Обещания содержат три состояния:

  • Выполнено
  • Отклоненный
  • В ожидании

Как работают обещания

Обещание - это объект, который может синхронно возвращаться из асинхронной функции.

Если вы запрашиваете что-то из программы (обещание), она остается в состоянии ожидания до тех пор, пока не будет завершена / завершена.

Как только условия выполнены, обещание разрешено, resolve(), или отклонено, reject(). Как только обещание исполнится, оно не может возобновиться.

Взгляните на этот фрагмент кода:

В этом коде мы передаем обещание, которое принимает два параметра: resolve и reject. Он может иметь любое имя, но эти примеры помогают нам лучше понять.

В этом примере предположим, что обещание выполнено - работа, которую он должен выполнить, завершена - и мы передаем его с помощью метода resolve().

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

Взгляните на этот фрагмент кода:

В этом фрагменте кода мы делаем то же самое, но предположим, что запрос / результат, который мы хотели получить из этого обещания, был отклонен / не выполнен.

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

Важные правила обещания

Стандарт обещания уже определен сообществом Promises / A + спецификация. Как правило, promises всегда следует определенному набору правил:

  • Объект обещания всегда должен предоставлять .then() метод.
  • Ожидающий promise всегда будет возвращаться либо к разрешению, либо к отклонению.
  • Обещание, разрешенное или отклоненное, всегда считается выполненным и не должно переходить в какое-либо другое состояние.
  • Как только обещание исполнится, оно приобретет некоторую ценность. Это значение не должно изменяться.

Что такое Async-Await?

До сих пор мы использовали обещания, чтобы справиться с асинхронностью в нашем коде. Это прекрасно работает, но новый выпуск async-await в ES8 оказался еще более полезным, позволив нам писать супергладкий, синхронно выглядящий код.

Согласно MDN (Сеть разработчиков Mozilla):

«Асинхронная функция - это функция, которая работает асинхронно через цикл событий, используя неявное обещание вернуть свой результат. Но синтаксис и структура вашего кода с использованием асинхронных функций больше похожи на стандартные синхронные функции ».

А теперь давайте разберемся в значении вышеприведенного предложения.

  • Async-await очень прост в использовании и, как и любая другая функция JavaScript, также работает асинхронно через цикл событий, который мы обсуждали ранее.
  • Он неявно связан с обещаниями и использует его для возврата результата.
  • Структура синтаксиса такая же, как при написании синхронной функции.

Асинхронный

Async-await - это просто продолжение обещаний. Это позволяет нам писать код на основе обещаний синхронно, не блокируя поток выполнения. async function означает, что обещанное значение вернется.

Он работает асинхронно через цикл событий. async function всегда возвращает значение. JavaScript автоматически возвращает разрешенное значение вместе с ним, если в качестве возврата не было возвращено обещание.

Чтобы понять это, давайте посмотрим на этот код:

Выполнение приведенного выше кода даст нам вывод предупреждения со строковым сообщением Hello World.

Это означает, что обработанное обещание успешно возвращается по умолчанию. Если бы было наоборот, метод .then() не работал бы.

Ждите

await keyword обрабатывает ожидающую часть блока async-await. Этот оператор используется для ожидания обещания.

Он работает внутри async block и ждет, пока обещание вернет результат. Await only заставляет ждать блок async function, а не всю программу.

Давайте поймем это, посмотрев на этот код:

Когда мы вызываем функцию display() в конце, она вызывает функцию AwaitExample(), заключенную в await block.

Это означает, что до тех пор, пока обещание не разрешится в функции AwaitExample(), await keyword будет удерживать выполнение методаdisplay().

Это не остановит выполнение программы, а будет содержать только метод display(). Через две секунды или 2000 миллисекунд обещание разрешается, что затем отображает консоль.

Правила, которым нужно следовать при использовании async-await

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

Давайте быстро взглянем:

  • Мы не можем использовать await keyword в обычных функциях. У вас должна быть async функция, чтобы использовать await внутри нее.
  • Async-await делает выполнение всегда последовательным. Параллельное выполнение должно быть быстрее.

Async-await очень мощный, но с оговорками. Если мы будем использовать их правильно, они помогут сделать наш код очень удобочитаемым и эффективным.

Последовательное и параллельное выполнение

Последовательное исполнение

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

Давайте разберемся в этом на примере. Взгляните на этот фрагмент кода:

В этом примере мы просто возвращаем две функции обещания. Но когда первый вызывает сам себя, на выполнение уходит две секунды. Между тем другая await функция просто сидит там.

Только после разрешения первого обещания выполняется следующее await start. Этот процесс занимает много времени по сравнению с параллельным выполнением.

Параллельное исполнение

Гораздо более эффективный способ обработки более быстрого выполнения - параллельное выполнение.

По сути, он не останавливает выполнение других await методов, если присутствует более одного метода.

Он параллельно обрабатывает все await методы. Этот тип обработки использует метод promise.all(). Давайте разберемся в этом с помощью этого примера:

В этом фрагменте кода мы используем метод promise.all(). Функция этого метода состоит в том, чтобы сначала разрешить все обещания внутри итерируемого объекта, а затем вернуть окончательный результат.

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

Обработка ошибок в Async-Await

Еще одна замечательная особенность асинхронной функции - это то, что async-await может обрабатывать ошибки синхронно, а также с помощью блока try and catch.

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

Давайте посмотрим на пример:

В этом примере мы видим, что Promise получает либо resolved , либо rejected в зависимости от случайного значения, сгенерированного переменной val .

Если значение равно единице, оно разрешается, а затем обрабатывается в блоке try. Если все наоборот, тогда JavaScript отклоняет обещание и обрабатывает себя в блоке catch, тем самым обеспечивая плавный поток выполнения на всем протяжении.

Это подводит нас к концу этой статьи. Надеюсь, вам понравилось узнать, какое влияние async-await оказывает на мир JavaScript прямо сейчас.

Спасибо за чтение. Мир снаружи. ✌️