Прокачайся в код-ревью: для первых 50 участников — курс бесплатный

время чтения: 3 мин

Асинхронные паттерны JavaScript и способы их реализации

В статье представлен краткий обзор концепции асинхронного программирования и асинхронных паттернов JavaScript — генераторов и итераторов — с примерами их реализации.

Автор статьи — EPAM Senior Software Engineer Анжани Тарайил.

Введение

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

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

Выполнение JavaScript

В JavaScript используется метод компиляции Just-In-Time (JIT). При первом запуске выделяется память под переменные и функции. При последующих запусках последовательно обрабатывается код. Таким образом, JavaScript представляет собой смесь интерпретации и компиляции, а не только одно или другое. 

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

  • На этапе создания выделяется память путем сканирования кода на наличие переменных и функций. 

  • Этап выполнения включает в себя собственно выполнение кода. 

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

  • Код выполняется последовательно, с единственным главным потоком. 

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

Как разобраться в асинхронном программировании

Асинхронное программирование является важнейшей составляющей современной веб-разработки. Этому способствует цикл событий JavaScript — основная концепция, обеспечивающая неблокирующее поведение.

Цикл событий

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

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

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

  • Стек выполнения: функции выполняются по очереди, образуя стек. 

  • Очередь сообщений: асинхронные задачи (события, веб-API) ожидают выполнения. 

  • Цикл событий: следит за стеком, извлекает задачи из очереди по одной, когда стек пуст. 

  • Выполнение задач: задачи выполняются по порядку, каждая завершается до начала выполнения следующей. 

  • Асинхронность: неблокирующие операции (обратные вызовы, обещания) позволяют планировать работу. 

  • Завершение: после завершения асинхронных задач их результаты попадают в очередь. 

  • Цикл событий продолжается: опустошает очередь в стек, сохраняя поток. 

  • Отзывчивые приложения: обеспечивает многозадачность, отзывчивость в приложениях.

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

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

Методы управления асинхронными операциями: 

  • Обратные вызовы: основа для асинхронной обработки, остерегайтесь ада обратных вызовов. 

  • Обещания: структурированный подход, цепные операции, использование .then() и .catch(). 

  • Async/Await: лаконичный синтаксис ES2017, асинхронные функции, ожидание обещаний. 

  • Генераторы и итераторы: функции с паузой, асинхронная итерация с помощью генераторов и итераторов.

Что такое генераторы и итераторы?

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

Генераторы

Генераторы представляют собой класс функций, которые могут быть остановлены и затем перезапущены. Они объявляются с помощью функции синтаксиса `function*`. Внутри функции генератора можно использовать ключевое слово `yield`, чтобы приостановить выполнение функции и сформировать значение для вызывающего. В дальнейшем функция может быть возобновлена с того места, где она была приостановлена. 

Пример функции-генератора:

JavaScript: пример функции-генератора

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

Итераторы

Итератор — это объект, определяющий последовательность и способ доступа к этой последовательности. В JavaScript итераторы создаются с помощью метода Symbol.iterator. Итератор должен реализовывать метод `next()`, возвращающий объект с двумя свойствами: `value` — текущее значение в последовательности; и `done` — указывающее, завершена ли последовательность. 

Базовый пример реализации итератора: 

JavaScript: пример функции-итератора

Генераторы могут быть использованы для упрощения создания итераторов, особенно при работе с асинхронными операциями. Использование ключевого слова `yield` в генераторе позволяет легко создавать значения управляемым и асинхронным образом. 

Пример асинхронного итератора на основе генератора: 

JavaScript: пример асинхронного итератора на основе генератора

В данном примере `asyncNumberGenerator` формирует значения, ожидая вызова асинхронной функции.

Заключение

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

 

Другие статьи по теме: