что такое асинхронная функция

Асинхронные функции 101

Одним из основных преимуществ JavaScript является то, что всё асинхронно. По большей части различные части вашего кода не влияют на выполнение других.

К сожалению, это также один из основных недостатков JavaScript. Задача выполнения синхронного кода становится сложнее, так как по умолчанию всё асинхронно.
Первым решением проблемы были колбэки. Если часть нашего кода зависит от какого-то результата, мы должны были вложить наш код —

Вложенные колбэки в колбэках, как мы знаем, становятся непригодными. Таким образом, были созданы промисы (Promise). Они позволили нам работать с синхронными кодом более чистым и плоским способом.

Как и всё, промисы тоже не идеальны. Таким образом, в рамках спецификации ES2017 был определен другой метод для работы с синхронным кодом: Асинхронные функции. Это позволяет нам писать асинхронный код, как если бы он был синхронным.

Создание асинхронной функции

Асинхронная функция определяется выражением асинхронной функции. Базовая функция выглядит так:

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

Использование Асинхронных функций

Асинхронные функции не являются заменой промисов. Они идентичны по своей природе. Так, асинхронная функция ожидает исполнения обещания и всегда возвращает промис.

Промис, возвращаемый асинхронной функцией, будет разрешен с любым значением, возвращаемым функцией.

Если будет выброшена ошибка, промис будет отклонён с этой ошибкой.

Выполнение Асинхронных функций в параллельно

С промисами мы можем выполнять несколько обещаний параллельно с помощью метода Promise.all ().

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

Это займет 1000 мс, так как второе ожидание не запустится, пока не завершится первое. Чтобы обойти это, мы должны ссылаться на функции таким образом:

Теперь это займет всего 500 мс, потому что обе функции pause500ms () выполняются одновременно.

Промисы или Асинхронные функции?

Как я уже упоминала, асинхронные функции не заменяют промисов. Они идентичны по своей природе. Асинхронные функции предоставляют альтернативный, а в некоторых случаях и лучший способ работы с основанными на промисах функциями. Но они всё ещё используют и производят промисы.

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

Поддержка

На момент написания статьи, асинхронные функции и промисы доступны в текущих версиях всех основных браузеров, за исключением Internet Explorer и Opera Mini.

Источник

Async/await

Существует специальный синтаксис для работы с промисами, который называется «async/await». Он удивительно прост для понимания и использования.

Асинхронные функции

У слова async один простой смысл: эта функция всегда возвращает промис. Значения других типов оборачиваются в завершившийся успешно промис автоматически.

Например, эта функция возвратит выполненный промис с результатом 1 :

Можно и явно вернуть промис, результат будет одинаковым:

Await

Ключевое слово await заставит интерпретатор JavaScript ждать до тех пор, пока промис справа от await не выполнится. После чего оно вернёт его результат, и выполнение кода продолжится.

В этом примере промис успешно выполнится через 1 секунду:

В данном примере выполнение функции остановится на строке (*) до тех пор, пока промис не выполнится. Это произойдёт через секунду после запуска функции. После чего в переменную result будет записан результат выполнения промиса, и браузер отобразит alert-окно «готово!».

Обратите внимание, хотя await и заставляет JavaScript дожидаться выполнения промиса, это не отнимает ресурсов процессора. Пока промис не выполнится, JS-движок может заниматься другими задачами: выполнять прочие скрипты, обрабатывать события и т.п.

Ошибки не будет, если мы укажем ключевое слово async перед объявлением функции. Как было сказано раньше, await можно использовать только внутри async –функций.

Давайте перепишем пример showAvatar() из раздела Цепочка промисов с помощью async/await :

Получилось очень просто и читаемо, правда? Гораздо лучше, чем раньше.

Можно обернуть этот код в анонимную async –функцию, тогда всё заработает:

В примере ниже, экземпляры класса Thenable будут работать вместе с await :

Для объявления асинхронного метода достаточно написать async перед именем:

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

Делает то же самое, что и такой:

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

Так сделано в строке (*) в примере выше.

Итого

Ключевое слово async перед объявлением функции:

Ключевое слово await перед промисом заставит JavaScript дождаться его выполнения, после чего:

Вместе они предоставляют отличный каркас для написания асинхронного кода. Такой код легко и писать, и читать.

Источник

Разбираемся с асинхронностью в JavaScript [Перевод статьи Sukhjinder Arora]

Привет, Хабр! Представляю вашему вниманию перевод статьи «Understanding Asynchronous JavaScript» автора Sukhjinder Arora.

что такое асинхронная функция. Смотреть фото что такое асинхронная функция. Смотреть картинку что такое асинхронная функция. Картинка про что такое асинхронная функция. Фото что такое асинхронная функция

От автора перевода: Надеюсь перевод данной статьи поможет вам ознакомиться с чем-то новым и полезным. Если статья вам помогла, то не поленитесь и поблагодарите автора оригинала. Я не претендую на звание профессионального переводчика, я только начинаю переводить статьи и буду рад любым содержательным фидбекам.

JavaScript — это однопоточный язык программирования, в котором может быть выполнено только что-то одно за раз. То есть, в одном потоке движок JavaScript может обработать только 1 оператор за раз.

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

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

Здесь то и вступает в игру асинхронность JavaScript. Используя асинхронность JavaScript(функции обратного вызова(callback’и), “промисы” и async/await) вы можете выполнять долгие сетевые запросы без блокирования основного потока.

Несмотря на то, что не обязательно изучать все эти концепции, чтобы быть хорошим JavaScript-разработчиком, полезно их знать.

Итак, без лишних слов, давайте начинать.

Как работает синхронный JavaScript?

Прежде чем мы углубимся в работу асинхронного JavaScript, давайте для начала разберемся как выполняется синхронный код внутри движка JavaScript. Например:

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

Контекст выполнения

Контекст выполнения — это абстрактное понятие окружения, в котором код оценивается и выполняется. Всякий раз, когда какой-либо код выполняется в JavaScript он запускается в контексте выполнения.

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

Стек вызовов

Под стеком вызовов подразумевается стек со структурой LIFO(Last in, First Out/Последний вошел, первый вышел), который используется для хранения всех контекстов выполнения, созданных на протяжении исполнения кода.

В JavaScript имеется только один стек вызовов, так как это однопоточный язык программирования. Структура LIFO означает, что элементы могут добавляться и удаляться только с вершины стека.

Давайте теперь вернемся к фрагменту кода выше и попробуем понять, как движок JavaScript его выполняет.

что такое асинхронная функция. Смотреть фото что такое асинхронная функция. Смотреть картинку что такое асинхронная функция. Картинка про что такое асинхронная функция. Фото что такое асинхронная функция

И так, что же здесь произошло?

Когда код начал выполняться, был создан глобальный контекст выполнения(представленный как main()) и добавлен на вершину стека вызовов. Когда встречается вызов функции first(), он так же добавляется на вершину стека.

Далее, на вершину стека вызовов помещается console.log(‘Hi there!’), после выполнения он удаляется из стека. После этого мы вызываем функцию second(), поэтому она помещается на вершину стека.

console.log(‘Hello there!’) добавлен на вершину стека и удаляется из него по завершению выполнения. Функция second() завершена, она также удаляется из стека.

console.log(‘The End’) добавлен на вершину стека и удален по завершению. После этого функция first() завершается и также удаляется из стека.

Выполнение программы заканчивается, поэтому глобальный контекст вызова(main()) удаляется из стека.

Как работает асинхронный JavaScript?

Теперь, когда мы имеем общее представление о стеке вызовов и о том, как работает синхронный JavaScript, давайте вернемся к асинхронному JavaScript.

Что такое блокирование?

Давайте предположим, что мы выполняем обработку изображения или сетевой запрос синхронно. Например:

Обработка изображения и сетевой запрос требует времени. Когда функция processImage() вызвана её выполнение потребует некоторого времени, в зависимости от размера изображения.

Когда функция processImage() выполнена она удаляется из стека. После нее вызывается и добавляется в стек функция networkRequest(). Это снова займет некоторое время прежде чем завершить выполнение.

В конце концов, когда функция networkRequest() выполнена, вызывается функция greeting(), поскольку она содержит только метод console.log, а этот метод, как правило, выполняется быстро, функция greeting() выполнится и завершится мгновенно.

Как вы видите, нам нужно ждать пока функция(такие как processImage() или networkRequest()) завершится. Это означает, что такие функции блокируют стек вызовов или основной поток. По итогу мы не можем выполнить другие операции, пока код выше не будет выполнен.

Так какое же решение?

Самое простое решение — это асинхронные функции обратного вызова. Мы используем их, чтобы сделать наш код неблокируемым. Например:

Здесь я использовал метод setTimeout для того чтобы имитировать сетевой запрос. Пожалуйста, помните, что setTimeout не является частью движка JavaScript, это часть так называемого web API(в браузере) и C/C++ APIs (в node.js).

Для того чтобы понять, как этот код выполняется, мы должны разобраться с ещё несколькими понятиями, такими как цикл обработки событий и очередь обратных вызовов(также известная как очередь задач или очередь сообщений).

что такое асинхронная функция. Смотреть фото что такое асинхронная функция. Смотреть картинку что такое асинхронная функция. Картинка про что такое асинхронная функция. Фото что такое асинхронная функция

Цикл обработки событий, web API и очередь сообщений/очередь задач не являются частью движка JavaScript, это часть браузерной среды выполнения JavaScript или среды выполнения JavaScript в Nodejs(в случае Nodejs). В Nodejs, web APIs заменяется на C/C++ APIs.

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

что такое асинхронная функция. Смотреть фото что такое асинхронная функция. Смотреть картинку что такое асинхронная функция. Картинка про что такое асинхронная функция. Фото что такое асинхронная функция

Когда код приведенный выше загружается в браузер console.log(‘Hello World’) добавляется в стек и удаляется из него по завершению выполнения. Далее встречается вызов функции networkRequest(), он добавляется на вершину стека.

Следующая вызывается функция setTimeout() и помещается на вершину стека. Функция setTimeout() имеет 2 аргумента: 1) функция обратного вызова и 2) время в миллисекундах.

setTimeout() запускает таймер на 2 секунды в окружении web API. На этом этапе, setTimeout() завершается и удаляется из стека. После этого, в стек добавляется console.log(‘The End’), выполняется и удаляется из него по завершению.

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

Цикл обработки событий

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

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

После console.log(‘Async Code’) добавляется на вершину стека, выполняется и удаляется из него. На этом моменте обратный вызов выполнен и удален из стека, а программа полностью завершена.

События DOM

Очередь сообщений также содержит обратные вызовы от событий DOM, такие как клики и “клавиатурные” события. Например:

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

Мы изучил как выполняются асинхронные обратные вызовы и события DOM, которые используют очередь сообщений для хранения обратных вызовов ожидающих своего выполнения.

ES6 Очередь микротасков

Прим. автора перевода: В статье автор использовал message/task queue и job/micro-taks queue, но если перевести task queue и job queue, то по идее это получается одно и то же. Я поговорил с автором перевода и решил просто опустить понятие job queue. Если у вас есть какие-то свои мысли на этот счет, то жду вас в комментариях

ES6 представил понятие очередь микротасков, которые используются “промисами” в JavaScript. Разница между очередью сообщений и очередью микротасков состоит в том, что очередь микротасков имеет более высокий приоритет по сравнению с очередью сообщений, это означает, что “промисы” внутри очереди микротасков будут выполняться раньше, чем обратные вызовы в очереди сообщений.

Как вы можете видеть, “промис” выполнился раньше setTimeout, все это из-за того, что ответ “промиса” хранится внутри очереди микростасков, которая имеет более высокий приоритет, нежели очередь сообщений.

Давайте разберем следующий пример, на этот раз 2 “промиса” и 2 setTimeout:

И снова оба наших “промиса” выполнились раньше обратных вызовов внутри setTimeout, так как цикл обработки событий считает задачи из очереди микротасков важнее задач из очереди сообщений/очереди задач.

Если во время выполнения задач из очереди микротасков появляется ещё один “промис”, то он будет добавлен в конец этой очереди и выполнен раньше обратных вызовов из очереди сообщений, и не важно сколько времени они ожидают своего выполнения.

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

Источник

Асинхронность в JavaScript: Пособие для тех, кто хочет разобраться

что такое асинхронная функция. Смотреть фото что такое асинхронная функция. Смотреть картинку что такое асинхронная функция. Картинка про что такое асинхронная функция. Фото что такое асинхронная функция

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

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

В JavaScript нет многопоточности. Несмотря на то, что мы уже можем полноценно использовать вебворкеры, из них нельзя менять DOM или вызывать методы объекта window. Одним словом, не многопоточность, а сплошное разочарование.

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

что такое асинхронная функция. Смотреть фото что такое асинхронная функция. Смотреть картинку что такое асинхронная функция. Картинка про что такое асинхронная функция. Фото что такое асинхронная функция

С DOM-деревом работают в одном потоке, чтобы гарантировать целостность и непротиворечивость данных, но как программировать интерфейс с одним потоком? Ведь сама суть интерфейса — в асинхронности. Именно для этого придуманы асинхронные функции. Они выполняются не сразу, а после наступления события. Интересно, что эти функции — не часть JavaScript-движков. Вызов setTimeout на чистом V8 приводит к ошибке, так как в V8 нет такой функции. Тогда откуда же появляется setTimeout или requestAnimationFrame или addEventListener?

Асинхронность внутри

Движок JavaScript похож на мясорубку, бесконечно перемалывающую операции, которые последовательно берутся из стека вызовов (1). Код выполняется линейно и последовательно. Удалить операцию из стека нельзя, можно только прервать поток выполнения. Поток выполнения прерывается, если вызвать что-то типа alert или «исключение».

что такое асинхронная функция. Смотреть фото что такое асинхронная функция. Смотреть картинку что такое асинхронная функция. Картинка про что такое асинхронная функция. Фото что такое асинхронная функция

Каждая операция содержит контекст — некую область памяти, из которой доступны данные. Контексты расположены в памяти в виде дерева. Каждому листу в дереве доступны области видимости, которые определены в родительских ветках и в корне (глобальной области видимости). Функции в JavaScript — это данные, они хранятся в памяти именно как данные и поэтому передаются как переменные или возвращаются из других функций.

Асинхронные операции выполняются не в движке, а в окружении (5,6). (Как подсказал forgotten это не совсем так: мы можем из стека вызовов сразу же положить функцию в очередь вызовов и таким образом чистый движок тоже будет работать асинхронно)
Окружение — надстройка на движком. NodeJS и Chrome для движка V8 и Firefox для Gecko. Иногда окружение еще называют web API.
Чтобы создать асинхронный вызов, в web API передается ссылка на функцию, которая выполнится позже или не выполнится вовсе.

У функции есть свой контекст или своя область памяти (3), в которой она определена. Функция имеет доступ к этой области памяти и ко всем родителям этой области памяти. Такие функции называются замыканиями. С этой точки зрения, все функции в JavaScript — замыкания, так как все они имеют контекст.

Web API и JavaScrtipt движок работают независимо. Web API решает, в какой момент функция двигается дальше, в очередь вызовов (2).

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

Окружение самостоятельно решает, когда добавить переданный ей код в очередь вызовов. Функции из очереди добавляются в стек выполнения (выполняются) не раньше, чем стек вызовов закончит работу над текущей функцией.
Таким образом, стек вызовов работает синхронно, а web API асинхронно.

Это очень важно! Разработчику не нужно самому контролировать параллельный доступ к ресурсам, асинхронную работу за него выполняет окружение. Окружения определяют различия между браузером и node.js, ведь на node.js мы пишем сетевые приложения или обращаемся напрямую к жесткому диску, а из Chrome перехватываем клики по кнопкам, используя один и тот же движок.

В очереди вызовов нельзя отменять отдельные операции. Это делается в окружении (removeEventListener — в качестве примера).

Примеры

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

Обработчик клика не сработает, а бесконечный цикл загрузит процессор компьютера. Вкладка зависнет 😉

Клик вызовет «тяжелую» для расчета функцию. После клика в консоль пишется start, в конце выполнения функции — end. Выполнение функции на моем ноутбуке занимает несколько секунд. Все время, пока выполняется функция, квадратик мигает. Это значит, что анимации в CSS выполняются асинхронно JavaScript-коду.

Но что будет, если вместо opacity менять размер?

Квадратик зависнет на время выполнения функции. Дело в том, что CSS-свойство height обращается к DOM. Как мы помним, к DOM можно обращаться только из одного потока, чтобы не было проблем с параллельным доступом.

Делаем вывод, что для анимации лучше пользоваться свойствами, которые не меняют DOM (transform, opacity и т.д.). А всю тяжелую работу в JavaScript лучше делать асинхронно. Например вот так.

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

Вывод

Благодаря JavaScript мы пишем асинхронные приложения, не задумываясь о многопоточности: о целостности и непротиворечивости данных. За эти преимущества мы платим огромным числом обратных вызовов, блокированием основного потока и постоянными потерями контекста.

О том, как бороться с последней проблемой, я расскажу в следующий раз.

Источник

Асинхронное программирование с async/await

Доброго времени суток, друзья!

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

Условия: базовая компьютерная грамотность, знание основ JS, понимание азов асинхронного кода и обещаний.
Цель: понять, как устроены обещания, и как они используются.

Основы async/await

Использование async/await состоит из двух частей.

Ключевое слово async

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

Попробуйте набрать в консоли браузера следующее:

Функция вернет ‘Hello’. Ничего необычно, верно?

Но что если мы превратим ее в асинхронную функцию? Попробуйте сделать следующее:

Теперь вызов функции возвращает обещание. Это одна из особенностей асинхронных функций — они возвращают значения, которые гарантировано преобразуются в обещания.

Вы также можете создать асинхронное функциональное выражения, например, так:

Также можно использовать стрелочные функции:

Все эти функции делают одно и тоже.

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

Ключевое слово await

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

Вы можете использовать await при вызове любой функции, возвращающей обещание, включая функции Web API.

Вот тривиальный пример:

Разумеется, приведенный код бесполезен, он лишь служит демонстрацией синтаксиса. Давайте двигаться дальше и посмотрим на реальный пример.

Переписываем код на обещаниях с использованием async/await

Возьмем пример с fetch из предыдущей статьи:

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

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

Вы можете переписать пример или запустить наше живое демо (см. также исходный код).

Но как это работает?

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

Еще раз: await работает только в асинхронных функциях.

Значение, возвращаемое обещанием fetch(), присваивается переменной response, когда данное значение становится доступным, и «парсер» останавливается на этой линии до завершения обещания. Как только значение становится доступным, парсер переходит к следующей строчке кода, которая создает Blob. Эта строчка также вызывает основанный на обещаниях асинхронный метод, поэтому здесь мы также используем await. Когда результат операции возвращается, мы возвращаем его из функции myFetch().

Добавляем обработку ошибок

Если вы хотите добавить обработку ошибок, у вас есть несколько вариантов.

Вы можете использовать синхронную структуру try. catch вместе с async/await. Этот пример является расширенной версией приведенного выше кода:

Блок catch()<> принимает объект ошибки, который мы назвали «e»; теперь мы можем вывести его в консоль, это позволит нам получить сообщение о том, в каком месте кода произошла ошибка.

Ожидание Promise.all()

Async/await основан на обещаниях, так что вы можете использовать все возможности последних. К ним, в частности, относится Promise.all() — вы легко можете добавить await к Promise.all(), чтобы записать все возвращаемые значения способом, похожим на синхронный код. Снова возьмем пример из предыдущей статьи. Держите вкладку с ним открытой, чтобы сравнить с показанным ниже кодом.

С async/await (см. живое демо и исходный код) он выглядит так:

Мы легко превратили функцию fetchAndDecode() в асинхронную с помощью парочки изменений. Обратите внимания на строчку:

Недостатки async/await

Async/await имеет парочку недостатков.

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

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

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

В нашем распоряжении имеется два примера — slow-async-await.html (см. исходный код) и fast-async-await.html (см. исходный код). Оба примера начинаются с функции-обещания, которая имитирует асинхронную операцию с помощью setTimeout():

Затем следует асинхронная функция timeTest(), которая ожидает трех вызовов timeoutPromise():

Каждый из трех вызовов timeTest() завершается записью времени выполнения обещания, затем записывается время выполнения всей операции:

В каждом случае функция timeTest() отличается.

В slow-async-await.html timeTest() выглядит так:

Здесь мы просто ожидаем три вызова timeoutPromise, каждый раз устанавливая задержку в 3 секунды. Каждый вызов ждет завершения предыдущего — если вы запустите первый пример, то увидите модальное окно примерно через 9 секунд.

В fast-async-await.html timeTest() выглядит так:

Здесь мы сохраняем три объекта Promise в переменных, что заставляет связанные с ним процессы выполняться одновременно.

Далее мы ожидаем их результаты — поскольку обещания начинают выполняться одновременно, обещания завершатся также в одно время; когда вы запустите второй пример, то увидите модальное окно примерно через 3 секунды!

Вам следует осторожно тестировать код и помнить об этом при снижении производительности.

Еще одним незначительным неудобством является необходимость оборачивания ожидаемых обещаний в асинхронную функцию.

Использование async/await совместно с классами

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

Метод класса может быть использован следующим образом:

Поддержка браузеров

Одним из препятствий использования async/await является отсутствие поддержки старых браузеров. Эта возможность доступна почти во всех современных браузерах, также как обещания; некоторые проблемы существуют в Internet Explorer и Opera Mini.

Если вы хотите использовать async/await, но нуждаетесь в поддержке старых браузеров, можете использовать библиотеку BabelJS — она позволяет использовать новейший JS, преобразуя его в подходящий для конкретного браузера.

Заключение

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

Источник

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

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