Концепция асинхронного программирования заключается в использовании функций, чей результат становится доступен лишь спустя определенный промежуток времени. В основе данного процесса лежат асинхронные вызовы процедур, которые идут вразрез со стандартным порядком выполнения. При запуске длительной операции, программа не дожидается ее завершения и продолжает выполняться.
Где используется асинхронность
Своей востребованностью в сфере веб-разработки, асинхронное программирование обязано крупным проектам и многопользовательским площадкам, включающим в себя сразу несколько различных сервисов. Данная концепция позволяет улучшить пользовательский опыт. Предположим, приложение находится в стадии загрузки информации с сервера. В это время на экране смартфона пользователь увидит блок с полезными советами или анимированное превью. При этом, главный поток продолжит получать данные с сервера.
За счет работы с методами асинхронного программирования можно значительно повысить производительность программного продукта, а также его отзывчивость, что положительно скажется на пользовательском опыте.
Асинхронность станет отличным решением для приложений, имеющим следующие особенности:
- программа подразумевает большое количество операций, связанных с вводом и выводом данных, из-за чего будет тратиться много времени на блокировки;
- в программе постоянно выполняется множество процессов, среди которых будет хотя бы одна операция, предполагающая работу во время блокировки всех остальных;
- большинство заданий изолированы, поэтому различные операции практически не обмениваются между собой данными.
Этим критериям соответствуют серверные приложения, чья работа сводится к постоянному ответу на пользовательские запросы. Из-за этого, в последнее время все больше проектов создается при помощи серверных платформ, умеющих функционировать в асинхронном режиме. Яркий пример такого продукта – Node.js.
Понятие многопоточности
Под ним понимают одновременное, независимое выполнение сразу нескольких потоков программы. Та же платформа Node работает с двумя типами потоков: основными и вспомогательными.
Первые – обрабатываются внутри циклов событий, вторые – в специальной подпрограмме, предназначенной для реализации операций ввода-вывода. Из-за них идет повышенная нагрузка на процессор. Обычно подобные операции являются следствием работы с системным диском или обработки сетевых запросов.
Принцип работы синхронных приложений
В случае с синхронными приложениями программы выполняется строка за строкой, то есть последовательно. Контроль окружению будет возвращен только после того, как программа полностью выполнится. Как правило это становится причиной различных сбоев и зависаний. Каждый пользователь хотя бы раз видел уведомление о том, что из-за работы определенного скрипта замедляется загрузка всего ресурса.
Проблему можно решить двумя способами – приостановить его загрузку или дождаться полного завершения. В обоих случаях пользователь, вероятнее всего, покинет сайт. Может случится так, что скрипт настолько замедлит работу браузера, что ОС просто не сможет обрабатывать действия посетителя до тех пор, пока приложение не завершится.
Асинхронность в языке JavaScript
Создавая программу на языке JavaScript, в нее можно добавить асинхронную обработку информации. Сделать это можно при помощи Async/Await, коллбэков или промисов. Каждый из этих вариантов имеет свои сильные и слабые стороны, поэтому выбор зависит от особенностей программы и реализуемой задачи.
Коллбэки
Это специальные функции, создаваемые для отложенного исполнения. Они срабатывают после окончания работы еще одной процедуры. Во многом их работа очень схожа с алгоритмом заказа обратного звонка. Если позвонить в службу техподдержки, скорее всего сработает автоответчик. Он предложит дождаться свободного оператора или сделать заказ на обратный звонок. Проще говоря, когда специалист освободится, он сам перезвонит клиенту и ответит на все вопросы.
Роль параметра в этом случае играет функция, которая вызывается позже, после завершения асинхронной части кода. К достоинствам данного метода относится:
- универсальность – коллбэки поддерживаются всеми браузерами и абсолютно всеми платформами;
- простота – чтобы все корректно работало, достаточно передать функцию, запуск которой произойдет позже.
Слабые стороны тоже есть. Прежде всего, это:
- нестандартная обработка ошибок – вместо общепринятых try/catch, процесс передачи ошибок зациклен;
- усложнение кода – в программе появляются многоуровневые вложения. Для этой ситуации был придуман специальный термин – callback hell;
- плохо читаемый код – без постоянного перехода между разными частями кода, невозможно разобраться в принципе вызова потоков.
Обещания (Promises)
Они представляют собой объекты, с помощью которых можно упорядоченно выполнять асинхронные вызовы. Они дают возможность оперативно отреагировать на ошибку, возникшую при выполнении, а также отложить определенное действие до момента завершения предшествующей функции. На сегодняшний день этот подход считается наиболее востребованным среди JavaScript-разработчиков.
К его достоинствам относят:
- гибкость при работе с масштабными асинхронными структурами – promises позволяет обрабатывать одновременно целый набор асинхронных вызовов. Для этого существует специальный метод promise.all;
- в отличии от обратных вызовов, промисы дают возможность отказаться от глубокой вложенности, благодаря тому, что их можно соединять в цепочки, тем самым обрабатывая сложно структурированные асинхронные потоки.
Обещания не лишены слабых сторон:
- пропущенные исключения – работа с возможными ошибками происходит при помощи метода catch. Если его не указать, часть браузеров будет просто пропускать все необработанные в promises исключения;
- ошибки связанные с API – программист должен быть на 100% уверен в том, что программа вернет промис. Если этого не произойдет, код, вероятнее всего, выдаст ошибку;
- отсутствие поддержки некоторыми браузерами – больше всего проблем вызывает Internet Explorer. Чтобы от них избавиться, необходимо специальное решение.
Async/Await
Это относительно новая технология. Она основывается на промисах, рассмотренных выше. Именно поэтому, ее довольно часто относят к категории синтаксического сахара. Иными словами, метод позволяет писать лаконичный и удобочитаемый код, работая при этом с уже знакомой концепцией.
Также, как и в случае promises, здесь нет блокировок исполнения. Из-за того, что код написанный в рамках данной концепции выглядит более похожим на стандартный синхронный, все больше разработчиков выбирают именно ее.
Для приостановки функции до момента завершения вызова Async используется Await. Поскольку Async-функции возвращают стандартный промис, с их помощью обрабатываются все ответы. Полученный результат асинхронной функции самостоятельно передается в метод promise.resolve.
Крайние обновления Node.js. имеют поддержку данной технологии, как и большинство современных браузеров. Исключение составляет Internet Explorer, для которого потребуется преобразование кода. Решить эту задачу поможет специальное ПО, например, компилятор Babel.
Преимущества
Одним из главных достоинств является возможность пользоваться стандартной конструкцией Try/Catch. Кроме этого написанный код гораздо легче воспринимать, поскольку вложенность сокращается. Вместо дополнительных вложений в код добавляется лишь еще одно слово Await. В случае с коллбэками и промисами код становится сложным для восприятия.
Среди прочего, разработчики отмечают более удобную отладку. Последовательность операций Async/Await позволяет создать более наглядное представление списка вызванных методов. При объединении промисов в цепочку, есть риск получения уведомления об ошибке.
Недостатки
Главным недостатком можно считать спад производительности. Используя данный метод, программист должен быть готов к тому, что его программа будет исполняться немного дольше. Так, Await заставляет приостановить исполнение, и асинхронные операции не смогут выполниться одновременно. Промисы, а именно promise.all позволяют выполнять сразу несколько асинхронных вызовов.
Как было сказано ранее, иногда код приходится преобразовывать, чтобы он корректно работал на устаревшем ПО. В результате этой операции он увеличивается в объеме. В случае с компилятором Babel количество строк увеличивается почти в пять раз.
Другие подходы
Чтобы добавить в JS-программу асинхронный функционал, можно воспользоваться генераторами, итераторами или специальной библиотекой, дающей возможность работать с асинхронными функциями. Эта библиотека называется RxJS, и она снимает ограничения, возникающие в результате работы с обещаниями.
При использовании promises, обработке подлежит всего одно событие, которое невозможно отменить. Если попытаться это сделать, поток блокируется и начинается неоправданная трата системных ресурсов. В случае с RxJS, есть несколько каналов, которые значительно упрощают процесс обработки операций. Поскольку понятия многопоточности и асинхронности тесно связаны с современными веб-приложениями, разработчик должен разбираться в этих технологиях и уметь применять их на практике.