Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
shass
7 лет назад

JavaScript Async/Await за 10 минут

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

К счастью, затем пришли промисы (Promise). Они предложили гораздо более организованную альтернативу колбэкам, и большая часть JavaScript-сообщества быстро перешла к их использованию.

Теперь же, с приходом Async/Await, написание JavaScript кода станет еще удобнее!

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

Async/Await - это долгожданная функция JavaScript, которая делает работу с асинхронными функциями более приятной и понятной. Он построен поверх Promises и совместим со всеми существующими API-интерфейсами на основе Promise.

Название происходит от async и await - двух ключевых слов, которые помогут нам очистить наш асинхронный код:

async - объявляет асинхронную функцию (async function someName(){...})

  • Автоматически преобразует обычную функцию в Promise.
  • Функции async резолвят (resolve) всё, что возвращается в их теле.
  • Асинхронные функции позволяют использовать await.

await - приостанавливает выполнение функции (let result = await someAsyncCall();)

  • Когда await помещен перед вызовом Promise, await приостанавливает выполнение кода, следующего за ним, заставляя скрипт ждать возвращения результата выполняемого Promise'а.
  • await работает только с Promise'ами, он не работает с обычными колбэками.
  • await может использоваться только внутри асинхронных функций.

Вот простой пример, который, надеюсь, прояснит ситуацию:

Предположим, мы хотим получить некоторый JSON-файл с сервера. Мы напишем функцию, которая использует библиотеку axios и отправляет HTTP-запрос GET для https://tutorialzine.com/misc/files/example.json
Для его получения, мы должны ждать ответа от сервера, поэтому, естественно, HTTP-запрос будет асинхронным.

Ниже мы видим, что одна и та же функция реализована дважды. Сначала с Promise, затем второй раз с использованием Async/Await.

// Вариант с Promise
function getJSON(){
// Чтобы блокировать функцию, создаем Promise
return new Promise( function(resolve) {
   axios.get('https://tutorialzine.com/misc/files/example.json')
       .then( function(json) {
           // Ответ от сервера доступен в блоке .then
           // Возвращаем результат с помощью функции resolve
           resolve(json);
       });
});}

// Вариант Async/Await
// Ключевое слово async автоматически создаст новый Promise и возвратит его
async function getJSONAsync(){ // Ключевое слово await позволяет нам не писать блок .then
let json = await axios.get('https://tutorialzine.com/misc/files/example.json');
// Ответ на GET запрос сохранен в переменной json
// Возвращаем его как в обычной синхронной функции
return json;
}

Явно, что версия на Async/Await короче и читается легче. Помимо используемого синтаксиса обе функции идентичны - они обе возвращают Promise и резолвятся с JSON ответом от axios. Мы можем вызвать async функцию вот так:

getJSONAsync().then( function(result) {
// Сделать что-нибудь с результатом.
});

Итак, делает ли Async/Await устаревшими Promise'ы?

Нет. При работе с Async/Await мы все еще используем Promise под капотом. Хорошее понимание Promise'ов, на самом деле, очень рекомендуется и поможет вам в долгосрочной перспективе.

Есть даже случаи, когда Async/Await не сокращают код, и приходится возвращаться к Promise за помощью. Один из таких случаев - это когда нам нужно сделать несколько независимых асинхронных вызовов и дождаться их завершения.

Если мы попытаемся сделать это с помощью async и ожидаем, произойдет следующее:

async function getABC() {
let A = await getValueA(); // getValueA займет 2 секунды
let B = await getValueB(); // getValueB займет 4 секунды
let C = await getValueC(); // getValueC займет 3 секунды
return A*B*C;
}

Каждый вызов await будет ждать, пока предыдущий не вернет результат. Поскольку мы выполняем один вызов за раз, вся функция займет 9 секунд от начала до конца (2 + 4 + 3).

Это не оптимальное решение, так как три переменные A, B и C не зависят друг от друга. Другими словами, нам не нужно знать значение A до того, как мы получим B. Мы можем получить их в одно и то же время и уменьшить время ожидания.

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

async function getABC() {
// Promise.all() позволяет выполнить все функции одновременно
let results = await Promise.all([ getValueA, getValueB, getValueC ]);
return results.reduce((total,value) => total * value);
}

Таким образом, функция займет гораздо меньше времени. Вызовы getValueA и getValueC будут уже завершены к концу getValueB. Таким образом мы сокращаем время исполнения скрипта до времени самого медленного (getValueB()), а не суммы всех взятых.

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

Еще одна отличная вещь в Async/Await заключается в том, что она позволяет нам обнаруживать любые неожиданные ошибки в старом блоке try/catch. Нам просто нужно обернуть наши await следующим образом:

async function doSomethingAsync(){
try {
// Этот запрос может вызваться с ошибкой
let result = await someAsyncCall();
}
catch(error) {
// Если была ошибка - перехват ошибки будет тут
}
}

Блок catch будет обрабатывать ошибки, вызванные ожидаемыми (await) асинхронными вызовами или любым другим падающим с ошибкой кодом, который мы запишем внутри блока try.
Если нужно, мы также можем перехватить ошибки при выполнении функции async. Поскольку все функции async возвращают Promise'ы, мы можем просто включить обработчик события .catch() при их вызове.

// Async функция без блока try/catch
async function doSomethingAsync(){
// Этот асинхронный запрос может вызвать ошибку
let result = await someAsyncCall();
return result;
}
// Перехватываем ошибку при вызове функции
doSomethingAsync().
.then(successHandler)
.catch(errorHandler);

Важно выбрать, какой метод обработки ошибок вы предпочитаете, и придерживаться его. Использование try/catch и .catch() одновременно, скорее всего, приведет к проблемам.

Поддержка браузерами

Async/Await уже доступен в большинстве основных браузеров. Исключение составляет только IE11 - все остальные производители поймут ваш код на async/await без необходимости использования полифилов. Можно убедиться в этом по ссылке http://caniuse.com/#search=await

Если же такая совместимость вам не подходит, тогда всегда можно прибегнуть к транспайлерам вроде Babel или TypeScript.

230
0.000 GOLOS
На Golos с July 2017
Комментарии (5)
Сортировать по:
Сначала старые