Suggest an editImprove this articleRefine the answer for “await inside an event handler”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Yes you can, but the handler must be declared `async`: `await` is only allowed inside an `async` function, otherwise you get a `SyntaxError`.** The browser calls the handler and does not wait for the promise it returns, so the UI is not blocked, but nobody sees an error either unless the asynchronous part is wrapped in `try/catch`. One more detail: `preventDefault()` and `stopPropagation()` must be called synchronously, before the first `await`. ```javascript button.addEventListener('click', async (event) => { event.preventDefault(); // synchronously, before the first await try { const res = await fetch('/api/data'); console.log(await res.json()); } catch (err) { console.error('Request failed:', err); } }); ``` **Key point:** `async` on the handler plus `try/catch` inside, and `preventDefault()` always before the first `await`.Shown above the full answer for quick recall.Answer (EN)Image**Yes, you can use `await` inside an event handler, but the handler itself has to be declared `async`.** It is an ordinary `async` function that the browser calls: it returns a promise nobody waits for, so the UI is never blocked, and handling errors is entirely your responsibility. ## Theory ### TL;DR - `await` is allowed **only inside a function marked `async`**, otherwise the engine throws a `SyntaxError`. - The working form is `element.addEventListener('click', async () => { ... })`. - The browser **does not wait** for the promise the handler returns and has no idea there is an `await` inside. - Errors from an `async` handler do not join the normal exception flow, so you need `try/catch`. - Call `preventDefault()` and `stopPropagation()` synchronously, before the first `await`. - It is convenient to move the logic into a separate `async` function and pass it as the handler. ### Quick example ```javascript // 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); }); ``` Now everything is legal: inside the handler execution waits for `fetch`, but the event itself does not freeze the page, and the rest of the code keeps running. ### The basic rule: await lives only inside async `await` is not a global construct, it is part of the syntax of `async` functions. If a handler written as a plain function or arrow contains `await`, the script will not even start: the error happens at parse time, not on click. There is exactly one exception, top level `await` in ES modules, and it does not apply to event handlers, because a handler body is always a function body. ### A practical example: loading data on click ```html <button id="load">Load data</button> <div id="result"></div> ``` ```javascript const btn = document.querySelector('#load'); const result = document.querySelector('#result'); btn.addEventListener('click', async () => { result.textContent = 'Loading...'; try { const res = await fetch('https://api.example.com/posts/1'); const post = await res.json(); result.textContent = post.title; } catch (err) { result.textContent = 'Failed to load'; } }); ``` What happens here: - the click starts the `async` handler; - `await fetch(...)` pauses execution **inside that function**; - once the data arrives it goes into the DOM; - the interface stays responsive, because JavaScript remains asynchronous and the event loop is free. ### Moving the logic into a separate async function Sometimes it is cleaner to keep the logic away from the subscription: ```javascript async function handleClick() { const data = await fetchData(); render(data); } button.addEventListener('click', handleClick); ``` This behaves identically: `addEventListener` simply receives a function reference, and the fact that it is `async` is an implementation detail. This style is also easier to test and reuse. ### What the browser does with the handler's promise This is the part interviewers are really asking about: - **Nothing awaits the handler.** `addEventListener` does not know there is an `await` inside. Code after the handler call runs immediately and the returned promise is simply discarded. - **You must catch errors yourself.** An unhandled rejection ends up in `unhandledrejection` and can easily go unnoticed: ```javascript btn.addEventListener('click', async () => { try { await riskyOperation(); } catch (e) { console.error('Error:', e); } }); ``` - **`preventDefault()` and `stopPropagation()` take effect instantly**, but only while the handler is still running synchronously. After the first `await` the browser has finished dispatching the event and the default action can no longer be cancelled, so call them at the very top. - **Double clicks.** While an `await` is pending the user can click again and start a second request. The fix is simple: disable the button or keep an `isLoading` flag. ### Summary table | Question | Answer | | --- | --- | | Can you use `await` in a handler? | Yes, if the handler is `async` | | Can you write `await` without `async`? | No, it is a `SyntaxError` | | Does it block the interface? | No, the event loop stays free | | Do you have to catch errors? | Yes, with `try/catch` | | Can the `async` function live separately? | Yes, and it is the cleaner style | | Does the browser wait for the handler to finish? | No, the returned promise is ignored | A simple rule: `await` inside a handler is fine, as long as the handler is `async` and the errors are handled. ### Common mistakes - **Forgetting `async` on the handler.** The script fails with a `SyntaxError` before the first click, and the page ends up with no handlers at all. - **Calling `preventDefault()` after an `await`.** The form has already been submitted and the link has already navigated: there is nothing left to cancel. - **Assuming the browser waits for the handler.** It does not, which is why asynchronous work inside `beforeunload` never gets the chance to finish. - **Not handling errors.** A rejected promise from a handler does not stop the program and often disappears silently, leaving the interface stuck on "Loading...". - **Not guarding against repeated clicks.** Several quick clicks fire several parallel requests, and the DOM ends up showing whichever finished last rather than whichever was clicked last. - **Marking a handler `async` for no reason.** With no asynchronous work inside, `async` only adds a microtask and makes the code harder to read.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.