await в обробнику події
Так, await в обробнику події використовувати можна, але сам обробник треба оголосити як async. Це звичайна async-функція, яку викликає браузер: вона повертає promise, на який ніхто не чекає, тому інтерфейс не блокується, а відповідальність за помилки повністю лежить на вас.
Теорія
TL;DR
awaitдозволений тільки всередині функції зasync, інакше двигун кинеSyntaxError.- Робочий варіант:
element.addEventListener('click', async () => { ... }). - Браузер не чекає на promise, який повернув обробник, і не знає, що всередині є
await. - Помилки з
async-обробника не потрапляють у звичайний потік винятків, тож потрібенtry/catch. preventDefault()іstopPropagation()викликайте синхронно, до першогоawait.- Логіку зручно виносити в окрему
async-функцію і передавати її як обробник.
Швидкий приклад
// Wrong: await inside a plain, non-async function.
button.addEventListener('click', () => {
await fetch('/api/data'); // SyntaxError
});
// Correct: the handler itself is async.
button.addEventListener('click', async () => {
const response = await fetch('/api/data');
const data = await response.json();
console.log(data);
});Тепер усе законно: всередині обробника виконання чекає на fetch, але сама подія не «заморожує» сторінку, решта коду продовжує працювати.
Базове правило: await живе лише в async
await це не глобальна конструкція, а частина синтаксису async-функцій. Якщо обробник, оголошений як звичайна функція чи стрілка, містить await, скрипт навіть не запуститься: помилка виникає на етапі парсингу, а не під час кліку.
Виняток лише один: top-level await у модулях ES, але до обробників подій це не стосується, бо тіло обробника завжди є тілом функції.
Практичний приклад: завантаження даних по кліку
<button id="load">Завантажити дані</button>
<div id="result"></div>const btn = document.querySelector('#load');
const result = document.querySelector('#result');
btn.addEventListener('click', async () => {
result.textContent = 'Завантаження...';
try {
const res = await fetch('https://api.example.com/posts/1');
const post = await res.json();
result.textContent = post.title;
} catch (err) {
result.textContent = 'Помилка завантаження';
}
});Що тут відбувається:
- по кліку запускається
async-обробник; await fetch(...)призупиняє виконання всередині цієї функції;- після завантаження дані потрапляють у DOM;
- інтерфейс при цьому не блокується, бо JavaScript залишається асинхронним, а цикл подій вільний.
Винесення логіки в окрему async-функцію
Іноді зручніше тримати логіку окремо від підписки на подію:
async function handleClick() {
const data = await fetchData();
render(data);
}
button.addEventListener('click', handleClick);Працює абсолютно так само: addEventListener просто отримує посилання на функцію, а те, що вона async, для нього деталь реалізації. Такий стиль легше тестувати й повторно використовувати.
Що браузер робить з promise обробника
Це найважливіша частина відповіді на співбесіді:
-
Обробник не чекають.
addEventListenerне знає, що всередині єawait. Код після виклику обробника виконується одразу, а повернений promise просто ігнорується. -
Помилки треба ловити самостійно. Необроблене відхилення піде в
unhandledrejectionі може лишитися непоміченим:javascriptbtn.addEventListener('click', async () => { try { await riskyOperation(); } catch (e) { console.error('Error:', e); } }); -
preventDefault()іstopPropagation()працюють миттєво, але тільки поки обробник виконується синхронно. Після першогоawaitбраузер уже завершив обробку події і скасувати дію за замовчуванням неможливо, тому викликайте їх на самому початку. -
Подвійні кліки. Поки триває
await, користувач може клікнути ще раз і запустити другий запит. Захист простий: вимкнути кнопку або тримати прапорецьisLoading.
Підсумкова таблиця
| Питання | Відповідь |
|---|---|
Чи можна використовувати await в обробнику? | Так, якщо обробник async |
Чи можна написати await без async? | Ні, буде SyntaxError |
| Чи блокує це інтерфейс? | Ні, цикл подій лишається вільним |
| Чи треба ловити помилки? | Так, через try/catch |
Чи можна винести async-функцію окремо? | Так, це навіть чистіший стиль |
| Чи чекає браузер на завершення обробника? | Ні, повернений promise ігнорується |
Просте правило: await в обробнику це нормально, головне щоб обробник був async, а помилки були оброблені.
Типові помилки
- Забути
asyncна обробнику. Скрипт падає зSyntaxErrorще до першого кліку, і сторінка залишається без жодних обробників. - Викликати
preventDefault()післяawait. Форма вже відправилась, посилання вже спрацювало: скасовувати нічого. - Покладатися на те, що браузер дочекається обробника. Він не дочекається, тому в
beforeunloadасинхронна робота просто не встигне виконатись. - Не обробляти помилки. Відхилений promise з обробника не зупиняє програму й часто зникає без сліду, лишаючи інтерфейс у стані «Завантаження...».
- Не блокувати повторні кліки. Кілька швидких кліків дають кілька паралельних запитів, і в DOM потрапляє результат того, який завершився останнім, а не останнього натиснутого.
- Робити обробник
asyncбез потреби. Якщо асинхронності немає, зайвийasyncлише додає мікрозадачу і ускладнює читання коду.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.