The event loop
JavaScript runs one task at a time on the main thread. Async work is delegated to the browser/Node, and callbacks are queued:
console.log("sync 1");
setTimeout(() => console.log("timeout"), 0);
Promise.resolve().then(() => console.log("microtask"));
console.log("sync 2");
// Output: sync 1, sync 2, microtask, timeout
Microtasks (Promises) run before the next macrotask (setTimeout). This ordering matters when both are scheduled.
Error handling
Unhandled rejections crash the process. Catch errors at the edge:
// Option 1: await + try/catch
try { await doThing(); } catch (e) { handleError(e); }
// Option 2: .catch on a promise
doThing().catch(handleError);
// Option 3: allSettled to ignore individual failures
await Promise.allSettled([a, b, c]);
Promise.all rejects if any input rejects; Promise.allSettled waits for all and reports each outcome, which is safer for independent requests.