Асинхронні патерни JavaScript і способи їх реалізації
У цій статті подано короткий огляд концепції асинхронного програмування та інформація про асинхронні патерни JavaScript — генератори та ітератори — з прикладами їх реалізації.
Автор цієї статті — EPAM Senior Software Engineer Анжані Тараїл.
Вступ
Ви належите до тих, хто почувається розгубленим, роздумуючи над тим, чи є JavaScript асинхронною або синхронною? Межа між цими поняттями може бути незрозумілою навіть для досвідчених розробників.
У цій статті я розповім про принципи, що лежать в основі виконання JavaScript, а також синхронну й асинхронну JavaScript. Потім я детальніше розгляну різні асинхронні патерни в JS.
Виконання JavaScript
JavaScript використовує метод компіляції Just-In-Time (JIT). Під час першого запуску вона виділяє пам’ять для змінних і функцій. У наступних запусках код обробляється послідовно. Це робить JavaScript сумішшю інтерпретації та компіляції, а не лише чимось одним.
Під час запуску JavaScript-коду формується глобальний контекст виконання, який складається з двох етапів: створення та виконання.
На етапі створення пам’ять виділяється шляхом сканування коду на наявність змінних і функцій.
Етап виконання передбачає власне виконання коду.
Команди виконуються одна за одною; наступний рядок виконується лише після завершення поточного.
Код виконується послідовно, з єдиним головним тредом.
Довгі виклики функцій (наприклад, мережеві запити) можуть заблокувати основний тред, що призведе до нечутливості.
Як розібратися в асинхронному програмуванні
Асинхронне програмування має дуже важливе значення для сучасної веброзробки. Цьому сприяє цикл подій JavaScript — основне поняття, що забезпечує поведінку неблокування.
Цикл подій
Уявіть JavaScript шеф-кухарем, який виконує замовленнями на завантаженій кухні. Замість того, щоб готувати одну страву за раз, він перемикається між завданнями, щоб швидше обслужити всіх. Аналогічно цикл подій JavaScript керує кількома завданнями, уникаючи блокування.
Цикл подій дає JavaScript змогу виконувати завдання одночасно, не блокуючи основний тред. Це важливо для операцій, що займають багато часу, як-от мережеві запити, таймери та взаємодія, а також для забезпечення швидкого реагування програми.
Ось загальний огляд того, як працює цикл подій:
Стек виконання: функції виконуються одна за одною, утворюючи стек.
Черга повідомлень: асинхронні завдання (події, веб-API) очікують на виконання.
Цикл подій: стежить за стеком, витягує завдання з черги по одному за раз, коли стек порожній.
Виконання завдань: завдання виконуються по порядку, кожне завершується до початку наступного.
Асинхронний: операції неблокування (зворотні виклики, обіцянки) дають змогу планувати.
Завершення: після завершення асинхронних завдань їх результати приєднуються до черги.
Цикл подій продовжується: спускає чергу в стек, підтримуючи потік.
Чутливі застосунки: забезпечує багатозадачність, чутливість у застосунках.
Ефективне керування асинхронною поведінкою має дуже важливе значення для уникнення перевантаження пам’яті декількома паралельними операціями.
Методи управління асинхронними операціями:
Зворотні виклики: основа асинхронної обробки, остерігайтеся пекла зворотних викликів.
Обіцянки: структурований підхід, ланцюгові операції, використання .then() і .catch().
Async/Await: стислий синтаксис ES2017, асинхронні функції, очікування обіцянок.
Генератори та ітератори: функції з призупиненням, асинхронна ітерація за допомогою генераторів та ітераторів.
Що таке генератори та ітератори?
Генератори та ітератори — це потужні асинхронні патерни в JavaScript, які допомагають керувати асинхронним потоком управління та працювати з асинхронними операціями в більш читабельному та керованому вигляді. Розглянемо кожне поняття окремо.
Генератори являють собою клас функцій, які можна зупинити, а потім перезапустити. Вони оголошуються з використанням синтаксису `function*`. У функції-генераторі можна використовувати ключове слово `yield`, щоб призупинити виконання функції та сформувати значення для того, хто викликає. Пізніше функцію можна відновити з того місця, де її було призупинено.
Приклад функції-генератора:
Генератори
Генератори являють собою клас функцій, які можна зупинити, а потім перезапустити. Вони оголошуються з використанням синтаксису `function*`. У функції-генераторі можна використовувати ключове слово `yield`, щоб призупинити виконання функції та сформувати значення для того, хто викликає. Пізніше функцію можна відновити з того місця, де її було призупинено.
Приклад функції-генератора:
Генератори особливо корисні для асинхронних операцій, оскільки вони дають змогу призупинити виконання, очікуючи завершення асинхронних завдань, а потім відновити його, коли завдання буде виконано.
Ітератори
Ітератор — це об’єкт, який визначає послідовність і спосіб доступу до цієї послідовності. У JavaScript ітератори створюються за допомогою методу Symbol.iterator. Ітератор повинен реалізовувати метод `next()`, який повертає об’єкт із двома властивостями: `value` — поточне значення в послідовності; і `done` — вказує, чи завершено послідовність.
Ось базовий приклад реалізації ітератора:
Генератори можна використовувати для спрощення створення ітераторів, особливо під час роботи з асинхронними операціями. За допомогою ключового слова `yield` у генераторі можна легко формувати значення контрольованим та асинхронним способом.
Ось приклад асинхронного ітератора на основі генератора:
У цьому прикладі `asyncNumberGenerator` формує значення, очікуючи на виклик асинхронної функції.
Висновок
Генератори та ітератори, особливо якщо їх використовують разом, надають потужний набір інструментів для керування асинхронними операціями та створення більш читабельного й зручного в обслуговуванні асинхронного коду.
Продовжуючи свій шлях у програмуванні, відкрийте для себе світ асинхронних патернів JavaScript, озброївшись знаннями про генератори та ітератори. Нехай ваш код виконується безперебійно, ваші застосунки будуть чутливими, а шлях у розробці — сповнений інновацій та успіху.
Iнші статті за темою: