Question presented to candidate: "Walk me through exactly how try, catch, and finally work in JavaScript -- including a case or two where the behavior might surprise someone."
What a strong answer should cover:
- try wraps code that might throw; if anything inside it throws, control jumps straight to catch with the thrown value, skipping the rest of the try block entirely.
- finally always runs -- on the success path, on the caught-error path, and even if try or catch contains a return statement.
- A genuinely surprising, verified edge case: a return statement inside finally overrides whatever try or catch already returned.
- try...catch only catches synchronous throws inside the try block, and a rejected promise that is explicitly awaited inside it -- it does NOT catch a throw from inside a setTimeout callback or an unawaited async function, since those run on a later turn, after the try block has already finished.
- Since ES2019, catch can be written with no bound parameter (catch { ... }) when the error value itself is not needed.
Clarifying questions expected:
- "Should I focus on synchronous error handling, or also cover how it interacts with async/await and promise rejections?"
Code / implementation expected: Yes -- a short, runnable snippet demonstrating a caught sync throw, finally always running, and an awaited rejection being caught.