От автора: если вы имели возможность просматривать современный код Javascript — высока вероятность того, что вы где-нибудь видели синтаксис async и await. Async await JavaScript — это, пожалуй, одно из самых популярных нововведений в языке. Async / await заставляет асинхронный код отображаться и вести себя как синхронный. Async / await основаны на promise.
Прежде чем мы перейдем к async / await, мы должны понять, что такое promise и какую роль они играют. Promise — это объект, представляющий возможное завершение или сбой асинхронной операции.
В любом случае, зачем нам асинхронный код?
Javascript — это однопоточный язык, это означает, что Javascript может делать только одну вещь одновременно. Представьте, что вы вызываете API синхронно и блокируете весь поток на время вызова API — нашим пользователям придется ждать 30 секунд или столько времени, сколько потребуется для разрешения сетевого запроса — о, нет!
Если вам интересно узнать больше — вот более подробное пояснение по асинхронному программированию с использованием Javascript.
Способ, который мы использовали для обработки асинхронного кода Javascript, был связан с обратными вызовами. Скорее всего, вы сталкивались с ними.
Что такое обратные вызовы?
Функция обратного вызова, также известная как функция высшего порядка, является функцией, которая передается другой функции. Функции являются гражданами высшего сословия в Javascript — это означает, что они могут быть переданы в качестве аргументов функциям.
$("#btn").click(function() { alert("Button Clicked"); });
Возможно, вы видели специфичный для jQuery код, подобный этому. Следующий код добавляет прослушиватель событий к кнопке и вызывает alert, когда он срабатывает. Где же здесь обратный вызов? — можете спросить вы. Это анонимная function в скобках функции click.
Обратные вызовы в двух словах: мы запускаем функцию, делаем что-то, а затем вызываем другую функцию.
Обратные вызовы — это не всегда плохо, они работали — и все еще работают. Но что произойдет, если у нас будет обратный вызов внутри обратного вызова внутри обратного вызова — вы поняли мою мысль. Это становится действительно грязным и медленным.
Эта проблема была названа «адом обратных вызовов».
Еще один пример.
Мы проверяем, есть ли у нас дыня внутри массива — если да, мы ее жуем, после этого мы выбрасываем дыню. Мы также обрабатываем исключения с помощью обратного вызова err.
Примечание: обратный вызов err в первом аргументе — это всегда в мире Node лучшая практика!
Просто чтобы немного пояснить ситуацию — я сделал предыдущий код как можно более читабельным, вот как он мог выглядеть:
Мини-ад обратных вызовов; технически, можно было бы исправить это с помощью анонимных функций стрелок, но все же это не идеальное решение.
Вы можете себе представить, пара дополнительных обратных вызовов, и мы на пути в ад обратных вызовов!
Promises приходят на помощь!
Promises — это чистый способ написания асинхронного кода. Promises имеют единственный аргумент, который является обратным вызовом. Обратный вызов имеет два аргумента: условия reject и resolve.
И если мы используем функцию стрелки, чтобы сократить код:
Внутри блока promise мы можем решить, когда разрешить и когда отклонить promise.
Внутри promise мы проверяем, есть ли у нас дыня. Если это так, давайте resolve функцию со значением, которое мы передаем внутрь resolve - мы можем передать в resolve буквально любое значение. Promises немедленно вызываются / разрешаются, поэтому мы видим console.log без вызова promise.
Promises вызываются и задаются в последовательности с помощью методов then и catch. Представьте это так: promise был разрешен со значением — что мы будем с ним делать?
Вот где в дело вступает синтаксис then и catch. Они оба являются обратными вызовами, принимающими один аргумент, который является возвращаемым значением, передаваемым внутри обещания. Вот как мы можем обработать отклонение promise:
Хорошо, теперь наш promise не очень динамичен — почему? Ну, потому что у нас есть выражение if, которое не является динамическим. Promises чрезвычайно мощны, когда заключены в функцию. Мы называем их функциями высшего порядка.
Вы заметили небольшие изменения? Мы обернули promise в функцию, которая в этом случае принимает один аргумент. Это дает promise большую гибкость. Мы можем передать promise любое условие, и на основании этого условия он будет отклонен или разрешен.
Вот полностью рабочий promise, которые разрешается.
И отклоненный promise.
Начинает выглядеть знакомо? Возможно, вы видели вызовы Axios API, как показано ниже. Пример вызова Axios + React API (на основе promise):
axios.get('https://medium.com/@wesharehoodies') .then(res => this.setState({user: res.data})) .catch(err => new Error(err))
Или вызов API Fetch. Пример вызова Fetch + react API (на основе promise):
fetch('https://medium.com/@wesharehoodies') .then(res => res.json()) .then(res => this.setState({user: res.data})) .catch(err => new Error(err))
Что у них общего? Ну, для начала promise. Они в основном используют promise «под капотом». Так же, как мы обернули наш promise внутри функции. Во-вторых, они оба — это асинхронный код. Promises естественно асинхронны. Вот как выглядит асинхронный вызов API:
Можно с уверенностью сказать, что promise намного лучше, чем обратные вызовы. Хотя у promise есть свои недостатки — они могут выйти из-под контроля довольно быстро. Что, если есть лучший способ, еще более чистый. Async/await!
https://codesandbox.io/s/p9mr3jzwp0?autoresize=1&expanddevtools=1&hidenavigation=1
Мы помечаем функцию, как async - внутри функции мы помечаем асинхронный код, как await - Javascript разрешает promise и затем переходит к следующей строке. Короче говоря, мы изменяем асинхронный код, чтобы он читался как синхронный, в то же время работая асинхронно.
Обратите внимание, что мы больше не вызываем конструктор promise и что у нас намного меньше методов then() и catch().
Как насчет обработки ошибок?
Наши текущие async / await не обрабатывает исключения. Это может привести к катастрофическим ошибкам, которые могут привести к сбою приложения. Try…catch приходит на помощь!
Блок try catch пытается выполнить код, и если он сталкивается с какими-либо исключениями / проблемами, то передает объект ошибки в блок catch и выполняет код внутри блока catch. Вот как будет выглядеть наша async/await с обработкой ошибок.
Это только один из многих примеров, есть много способов использовать async / await. Если вам интересно — вот довольно хороший пример на StackOverflow, исследующий альтернативы.
Автор: Indrek Lasn
Источник: https://medium.freecodecamp.org/
Редакция: Команда webformyself.