[{"data":1,"prerenderedAt":333},["ShallowReactive",2],{"topic-javascript-async":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fjavascript.yml","Core JavaScript interview questions covering fundamentals, functions and closures, objects and prototypes, classes and OOP, and asynchronous programming with the event loop.","yml","javascript",{},"JavaScript",1,"frameworks\u002Fjavascript",0,"_TjxyYWBq7dftU9YakX_WX1Z4wAHq9uEUUW7wXL0y0c",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Fjavascript-async.yml","Callbacks, promises, async\u002Fawait and the event loop — how JavaScript handles non-blocking work behind the scenes.",{},"Asynchronous JavaScript",3,"async","topics\u002Fjavascript-async","N36EMzo9wWwCjQeB0JVso1oDGm5N7nWY0Cb9ec38Ph0",[25,186],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":11,"path":39,"questions":40,"related":179,"seo":180,"seoDescription":181,"stem":182,"subtopic":183,"topic":19,"topicSlug":21,"updated":184,"__hash__":185},"qa\u002Fjavascript\u002Fasync\u002Fpromises.md","Promises",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Fjavascript\u002Fasync\u002Fpromises",[41,46,50,54,58,62,67,71,75,79,83,87,91,95,99,103,107,111,115,119,123,127,131,135,139,143,147,151,155,159,163,167,171,175],{"id":42,"difficulty":43,"q":44,"a":45},"what-is-promise","easy","What is a Promise?","A Promise is an object representing the **eventual** result of an asynchronous\noperation — a placeholder for a value you don't have yet. It lives in one of\nthree states:\n\n- `pending` — the starting state, not yet settled.\n- `fulfilled` — completed successfully, carries a value.\n- `rejected` — failed, carries a reason (usually an `Error`).\n\n```js\nconst p = new Promise((resolve, reject) => {\n  setTimeout(() => resolve('done'), 100) \u002F\u002F settles after 100ms\n})\np.then(value => console.log(value)) \u002F\u002F 'done'\n```\n\nTwo properties make promises predictable: they're **settled once** (a\npending promise transitions to fulfilled *or* rejected exactly one time and\nthen its state\u002Fvalue are **immutable**), and `.then`\u002F`.catch` always run\n**asynchronously** as microtasks — never synchronously, even for an\nalready-resolved promise.\n",{"id":47,"difficulty":35,"q":48,"a":49},"async-await","What is async\u002Fawait and how does it relate to promises?","`async`\u002F`await` is **syntactic sugar over promises** — it doesn't add new\ncapabilities, it makes promise code read like ordinary sequential code. Two\nrules: an `async` function **always returns a promise** (any value you return\nis wrapped in a resolved promise; any throw becomes a rejected one), and\n`await` **pauses** the function until the awaited promise settles, then\nresumes with its value.\n\n```js\nasync function load() {\n  const res = await fetch('\u002Fapi')   \u002F\u002F pause until the request resolves\n  return res.json()                 \u002F\u002F also awaited by the caller\n}\nload().then(data => console.log(data)) \u002F\u002F still a promise underneath\n```\n\nCrucially, `await` only suspends the *current async function* — the rest of\nthe program keeps running. Under the hood the code after an `await` becomes a\n`.then` continuation scheduled as a microtask.\n",{"id":51,"difficulty":35,"q":52,"a":53},"error-handling","How do you handle errors with promises and async\u002Fawait?","With raw promises, attach `.catch()` to handle a rejection anywhere up the\nchain. With `async`\u002F`await`, an awaited rejected promise **throws**, so you use\na normal `try\u002Fcatch`.\n\n```js\n\u002F\u002F promise style\nload().then(use).catch(err => console.error(err))\n\n\u002F\u002F async\u002Fawait style — reads like synchronous try\u002Fcatch\ntry {\n  const data = await load()\n  use(data)\n} catch (err) {\n  console.error(err)\n} finally {\n  hideSpinner() \u002F\u002F runs whether it succeeded or failed\n}\n```\n\nGotchas interviewers probe: a single `.catch()` at the end catches errors from\n*every* prior step in the chain; an **unhandled** rejection triggers an\n`unhandledrejection` event\u002Fwarning; and forgetting to `await` (or `return`) a\npromise inside a `try` means its rejection escapes the `catch` entirely.\n",{"id":55,"difficulty":35,"q":56,"a":57},"promise-all","What is the difference between Promise.all and Promise.allSettled?","Both take an iterable of promises and run them concurrently, but they differ in\nhow they handle failure:\n\n- `Promise.all` **short-circuits**: it fulfills with an array of all values\n  once **every** promise fulfills, but rejects **immediately** the moment\n  **any one** rejects — you lose the results of the others.\n- `Promise.allSettled` **never rejects**: it waits for **every** promise to\n  settle and resolves with an array of `{ status, value }` or\n  `{ status, reason }` objects, so you can inspect each outcome.\n\n```js\nconst results = await Promise.allSettled([fetchA(), fetchB(), fetchC()])\nresults.forEach(r => {\n  if (r.status === 'fulfilled') console.log('ok', r.value)\n  else console.log('failed', r.reason)\n})\n```\n\nReach for `all` when you need *all-or-nothing* (one failure should abort);\nreach for `allSettled` when you want every result regardless of partial\nfailures (e.g. a dashboard of independent widgets).\n",{"id":59,"difficulty":35,"q":60,"a":61},"promise-race","What do Promise.race and Promise.any do?","Both settle based on the **first** promise to finish, but they disagree on what\ncounts:\n\n- `Promise.race` settles as soon as the first promise **settles** — whether it\n  fulfills *or* rejects. The first one to cross the line wins, error or not.\n- `Promise.any` resolves with the first promise to **fulfill**, ignoring\n  rejections. It only rejects if **all** of them reject, with an\n  `AggregateError` bundling every reason.\n\n```js\n\u002F\u002F timeout pattern — race the work against a timer\nconst data = await Promise.race([\n  fetch('\u002Fapi'),\n  new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), 5000)),\n])\n\n\u002F\u002F first successful mirror wins; only fails if every mirror is down\nconst fastest = await Promise.any([fetch(mirror1), fetch(mirror2)])\n```\n\nMnemonic: **race** cares about *first to finish*, **any** cares about *first to\nsucceed*.\n",{"id":63,"difficulty":64,"q":65,"a":66},"sequential-parallel","hard","How do you run async tasks in parallel vs sequentially?","The difference is **when you start** each task. `await` inside a loop starts\nthe next task only after the previous one resolves — that's **sequential** and\noften an accidental performance bug. To go **parallel**, kick them all off\nfirst (so they run concurrently), then `await` them together.\n\n```js\n\u002F\u002F sequential: ~A + B + C total time\nfor (const url of urls) {\n  results.push(await fetch(url))\n}\n\n\u002F\u002F parallel: ~max(A, B, C) total time\nconst results = await Promise.all(urls.map(url => fetch(url)))\n```\n\nUse sequential intentionally when each step **depends on the previous one's\nresult** or you must avoid hammering a rate-limited API. Use parallel when the\ntasks are independent — it can be dramatically faster.\n",{"id":68,"difficulty":64,"q":69,"a":70},"microtask","Why does an awaited promise callback run before a setTimeout?","Because promise continuations are scheduled as **microtasks**, while\n`setTimeout` callbacks are **macrotasks** — and the event loop drains the\n*entire* microtask queue after the current synchronous code, *before* it ever\nreaches the next macrotask.\n\n```js\nconsole.log(1)\nsetTimeout(() => console.log(2), 0)        \u002F\u002F macrotask\nPromise.resolve().then(() => console.log(3)) \u002F\u002F microtask\nconsole.log(4)\n\u002F\u002F Output: 1, 4, 3, 2 — the promise (3) beats the timer (2)\n```\n\nSo even `setTimeout(fn, 0)` is effectively \"run after all pending microtasks,\"\nwhich is why a resolved promise's `.then` always fires first.\n",{"id":72,"difficulty":35,"q":73,"a":74},"promisify","How do you convert a callback-based API to a promise?","Wrap the callback API in `new Promise(...)` and translate the callback's\noutcome into `resolve`\u002F`reject`. For Node-style `(err, value)` callbacks, the\nconvention is \"error first\": reject on `err`, otherwise resolve with the value.\n\n```js\n\u002F\u002F simple timer\nconst wait = ms => new Promise(resolve => setTimeout(resolve, ms))\n\n\u002F\u002F wrapping a Node-style error-first callback\nconst readFileAsync = path =>\n  new Promise((resolve, reject) => {\n    fs.readFile(path, 'utf8', (err, data) => {\n      if (err) reject(err)\n      else resolve(data)\n    })\n  })\n```\n\nIn Node you'd usually reach for the built-in `util.promisify` instead of\nhand-rolling this, but interviewers like to see you can build it from scratch.\n",{"id":76,"difficulty":35,"q":77,"a":78},"chaining","How does promise chaining work?","Each `.then` returns a **new promise** that resolves with whatever its callback\nreturns, so you can chain steps where each receives the previous result. This\nflattens nested callbacks into a linear sequence.\n\n```js\nfetch('\u002Fapi\u002Fuser')\n  .then(res => res.json())     \u002F\u002F returns parsed body\n  .then(user => fetch(`\u002Fapi\u002Fposts\u002F${user.id}`))\n  .then(res => res.json())\n  .then(posts => console.log(posts))\n  .catch(err => console.error(err))\n```\n\nThe chain runs step by step; returning a value passes it down, returning a\npromise waits for it. A single trailing `.catch` handles errors from any step.\n",{"id":80,"difficulty":35,"q":81,"a":82},"then-return-value","What does .then return and why does it matter?","`.then` always returns a **new promise**. What that promise resolves to depends on\nthe callback: a plain value -> resolves with it; a promise -> **adopts** that\npromise's eventual value (flattening); a `throw` -> rejects.\n\n```js\nPromise.resolve(1)\n  .then(x => x + 1)               \u002F\u002F resolves 2\n  .then(x => Promise.resolve(x*10)) \u002F\u002F resolves 20 (adopted)\n  .then(x => { throw new Error() }) \u002F\u002F rejects\n  .then(x => console.log('skipped'))\n  .catch(() => console.log('caught'))\n```\n\nBecause each `.then` is a fresh promise, chaining and error propagation \"just\nwork\" — and forgetting to **return** inside a `.then` breaks the chain (the next\nstep gets `undefined`).\n",{"id":84,"difficulty":35,"q":85,"a":86},"catch-chain","Where should you place .catch in a chain?","Usually a **single `.catch` at the end** of the chain — it handles a rejection\nfrom **any** preceding step, because errors propagate down until caught. Place an\nintermediate `.catch` only if you want to **recover** and continue the chain.\n\n```js\nstep1().then(step2).then(step3)\n  .catch(err => console.error('any step failed', err))\n\n\u002F\u002F recover mid-chain:\nload().catch(() => fallback()).then(use) \u002F\u002F continues with fallback's value\n```\n\nA `.catch` that returns a value produces a **resolved** promise, so the chain\ncontinues normally after recovery.\n",{"id":88,"difficulty":43,"q":89,"a":90},"finally-block","What does .finally do?","`.finally(cb)` runs `cb` **whether the promise fulfilled or rejected** — for\ncleanup that should always happen (hide a spinner, close a connection). It\nreceives **no argument** and, importantly, **passes the original value\u002Ferror\nthrough** unchanged.\n\n```js\nshowSpinner()\nfetchData()\n  .then(use)\n  .catch(handle)\n  .finally(() => hideSpinner()) \u002F\u002F always runs, doesn't alter the result\n```\n\nCaveat: if `.finally`'s callback itself throws or returns a rejected promise, it\n*does* override the outcome — but a normal return is ignored.\n",{"id":92,"difficulty":64,"q":93,"a":94},"unhandled-rejection","What is an unhandled promise rejection?","A rejected promise with **no `.catch`** (or no `try\u002Fcatch` around its `await`) is\n\"unhandled.\" Browsers fire an `unhandledrejection` event and log a warning; Node\nprints a warning and (in recent versions) **crashes the process** by default.\n\n```js\nPromise.reject(new Error('boom')) \u002F\u002F no handler -> unhandled rejection\n\nwindow.addEventListener('unhandledrejection', e => {\n  console.error('unhandled:', e.reason)\n  e.preventDefault()\n})\n```\n\nAlways attach a `.catch` or wrap awaits in `try\u002Fcatch`. A global handler is a\nsafety net for logging, not a substitute for handling errors at the source.\n",{"id":96,"difficulty":43,"q":97,"a":98},"resolve-reject-static","What do Promise.resolve and Promise.reject do?","They create **already-settled** promises: `Promise.resolve(v)` returns a promise\nfulfilled with `v` (or adopts `v` if it's already a promise\u002Fthenable), and\n`Promise.reject(e)` returns one rejected with `e`. Handy for returning a\nconsistent promise type or starting a chain.\n\n```js\nfunction getUser(id) {\n  const cached = cache.get(id)\n  if (cached) return Promise.resolve(cached) \u002F\u002F sync value as a promise\n  return fetch(`\u002Fapi\u002F${id}`).then(r => r.json())\n}\n```\n\n`Promise.resolve` is also how `await` wraps non-promise values, and how you\nnormalize \"maybe a value, maybe a promise\" into always-a-promise.\n",{"id":100,"difficulty":64,"q":101,"a":102},"thenable","What is a thenable?","A thenable is **any object with a `.then(resolve, reject)` method**. The promise\nmachinery treats thenables like promises — `await` and `Promise.resolve` will\n\"follow\" a thenable by calling its `then`. This is what makes different promise\nlibraries interoperate.\n\n```js\nconst thenable = {\n  then(resolve) { resolve(42) }\n}\nPromise.resolve(thenable).then(v => console.log(v)) \u002F\u002F 42\nawait thenable                                       \u002F\u002F 42\n```\n\nIt's why returning a thenable from `.then` flattens it. The downside: any object\nthat *happens* to have a `then` method gets treated specially — a rare source of\nsurprises.\n",{"id":104,"difficulty":35,"q":105,"a":106},"throw-in-then","What happens if you throw inside a .then callback?","Throwing inside a `.then` (or `.catch`) callback **rejects** the promise that\n`.then` returns, so the error skips subsequent `.then`s and is caught by the next\n`.catch` down the chain.\n\n```js\nPromise.resolve()\n  .then(() => { throw new Error('fail') })\n  .then(() => console.log('skipped'))      \u002F\u002F not run\n  .catch(err => console.log('caught:', err.message)) \u002F\u002F caught: fail\n```\n\nThis is what makes promise error handling feel like synchronous `try\u002Fcatch` — a\nthrown error propagates to the nearest handler instead of crashing.\n",{"id":108,"difficulty":35,"q":109,"a":110},"return-promise-then","What happens when you return a promise from .then?","The outer chain **waits** for that returned promise to settle and adopts its\nresult — this is \"flattening.\" It's how you sequence async steps without nesting.\n\n```js\ngetUser()\n  .then(user => fetchPosts(user.id)) \u002F\u002F returns a promise\n  .then(posts => console.log(posts)) \u002F\u002F runs only after fetchPosts resolves\n```\n\nPromises never nest as `Promise\u003CPromise\u003CT>>`; returning a promise from `.then`\nauto-unwraps it. Forgetting to **return** it, though, means the next step runs\nimmediately with `undefined`.\n",{"id":112,"difficulty":35,"q":113,"a":114},"nested-vs-flat","How do you flatten nested .then callbacks?","Nesting `.then`s recreates callback hell. Because returning a promise flattens the\nchain, you can keep it **flat** by returning instead of nesting.\n\n```js\n\u002F\u002F nested (\"promise hell\")\na().then(x => {\n  b(x).then(y => {\n    c(y).then(z => console.log(z))\n  })\n})\n\u002F\u002F flat\na().then(b).then(c).then(z => console.log(z))\n```\n\n`async\u002Fawait` flattens it even more readably. Nesting is occasionally needed when\nan inner step requires an **outer** value, but usually a flat chain (or await)\navoids it.\n",{"id":116,"difficulty":64,"q":117,"a":118},"reduce-sequential","How do you run promises sequentially using reduce?","Chain them by reducing over an array, where each step awaits the accumulator\npromise before starting — guaranteeing order and one-at-a-time execution.\n\n```js\nconst urls = ['\u002Fa', '\u002Fb', '\u002Fc']\nawait urls.reduce(\n  (chain, url) => chain.then(() => fetch(url)),\n  Promise.resolve()\n)\n\u002F\u002F or with await in a for...of:\nfor (const url of urls) await fetch(url)\n```\n\nUse this when each request must finish before the next (ordering, rate limits,\ndependencies). For independent requests, `Promise.all` is faster.\n",{"id":120,"difficulty":64,"q":121,"a":122},"concurrency-limit","How do you limit the concurrency of many async tasks?","Running thousands of requests at once with `Promise.all` can overwhelm a server\nor hit rate limits. Cap the number in flight with a **worker pool**: N workers\npulling from a shared queue.\n\n```js\nasync function pool(items, limit, fn) {\n  const results = []\n  const queue = [...items.entries()]\n  async function worker() {\n    for (const [i, item] of queue.splice(0)) results[i] = await fn(item)\n  }\n  await Promise.all(Array.from({ length: limit }, worker))\n  return results\n}\nawait pool(urls, 5, fetch) \u002F\u002F at most 5 concurrent requests\n```\n\nLibraries like `p-limit` provide this off the shelf. The idea: parallelism, but\n**bounded**.\n",{"id":124,"difficulty":64,"q":125,"a":126},"retry-backoff","How do you implement retry with exponential backoff?","Wrap the operation in a loop that catches failures and waits an increasing delay\nbefore retrying, up to a max attempt count.\n\n```js\nasync function retry(fn, attempts = 3, delay = 200) {\n  for (let i = 0; i \u003C attempts; i++) {\n    try {\n      return await fn()\n    } catch (err) {\n      if (i === attempts - 1) throw err\n      await new Promise(r => setTimeout(r, delay * 2 ** i)) \u002F\u002F 200, 400, 800...\n    }\n  }\n}\n```\n\nExponential backoff (often with jitter) avoids hammering a struggling service.\nOnly retry **idempotent**\u002Ftransient failures — retrying a non-idempotent write\ncan duplicate effects.\n",{"id":128,"difficulty":35,"q":129,"a":130},"for-await-of","What is for await...of and when do you use it?","`for await...of` iterates an **async iterable** (or an iterable of promises),\nawaiting each value in turn — ideal for consuming streams or paginated APIs\nsequentially.\n\n```js\nfor await (const chunk of readableStream) {\n  process(chunk) \u002F\u002F each chunk awaited in order\n}\n\nfor await (const value of [fetch('\u002Fa'), fetch('\u002Fb')]) {\n  console.log(value) \u002F\u002F awaits each promise sequentially\n}\n```\n\nIt processes items **one at a time** (sequential). For concurrent processing of\na fixed array, `Promise.all(arr.map(...))` is the right tool instead.\n",{"id":132,"difficulty":35,"q":133,"a":134},"top-level-await","What is top-level await?","In **ES modules**, you can use `await` at the **top level**, outside any async\nfunction. The module's evaluation pauses until the awaited promise settles, and\nimporters wait for it.\n\n```js\n\u002F\u002F config.mjs\nconst res = await fetch('\u002Fconfig.json')\nexport const config = await res.json()\n```\n\nIt's great for module initialization (loading config, dynamic imports,\nconnecting). Caveat: it can **delay** the loading of dependent modules, so avoid\nslow top-level awaits in widely-imported modules. Not available in CommonScript\nscripts.\n",{"id":136,"difficulty":64,"q":137,"a":138},"cancellation","How do you cancel a promise or async operation?","Promises themselves **can't be cancelled** — once started they run to settlement.\nThe standard approach is an **`AbortController`**: pass its `signal` to\ncancellable APIs (`fetch`, timers) and call `abort()` to stop them.\n\n```js\nconst ctrl = new AbortController()\nfetch('\u002Fslow', { signal: ctrl.signal })\n  .catch(e => { if (e.name === 'AbortError') console.log('cancelled') })\nctrl.abort() \u002F\u002F triggers an AbortError rejection\n```\n\nFor your own async functions, check `signal.aborted` at await points and bail\nout. The promise still settles (as a rejection); \"cancellation\" means stopping\nthe underlying work and ignoring the result.\n",{"id":140,"difficulty":64,"q":141,"a":142},"await-in-foreach","Why doesn't await work inside forEach?","`Array.prototype.forEach` **ignores** the return value of its callback, so it\ndoesn't await the async callback's promise — the loop fires all callbacks and\nmoves on immediately, not waiting for any of them.\n\n```js\n\u002F\u002F does NOT wait; \"done\" logs before any item is processed\nitems.forEach(async item => { await process(item) })\nconsole.log('done')\n\n\u002F\u002F sequential\nfor (const item of items) await process(item)\n\u002F\u002F concurrent\nawait Promise.all(items.map(item => process(item)))\n```\n\nUse `for...of` (sequential) or `map` + `Promise.all` (concurrent). `forEach` is\nsimply not async-aware.\n",{"id":144,"difficulty":64,"q":145,"a":146},"await-parallel-start","How do you start async tasks in parallel but await them later?","Call the async functions **first** (which starts them immediately) and store the\npromises, then `await` them afterward. The work overlaps because it began before\nany `await`.\n\n```js\nconst pa = fetchA() \u002F\u002F starts now\nconst pb = fetchB() \u002F\u002F starts now (concurrently)\nconst a = await pa  \u002F\u002F wait for both, but they already ran in parallel\nconst b = await pb\n```\n\nContrast with `const a = await fetchA(); const b = await fetchB()` which is\nsequential. `Promise.all([pa, pb])` is the cleaner equivalent and also propagates\nerrors as soon as one fails.\n",{"id":148,"difficulty":35,"q":149,"a":150},"parallel-map-all","How do you process an array concurrently with Promise.all and map?","Map each element to a promise (starting all the work), then `Promise.all` to wait\nfor the whole batch and collect results **in order**.\n\n```js\nconst results = await Promise.all(\n  ids.map(id => fetchUser(id))\n)\n\u002F\u002F results[i] corresponds to ids[i], even if they resolved out of order\n```\n\nThis runs everything concurrently — far faster than awaiting in a loop. Beware\nunbounded concurrency for large arrays (use a concurrency limit), and remember\n`Promise.all` rejects as soon as **any** task fails.\n",{"id":152,"difficulty":35,"q":153,"a":154},"explicit-construction-antipattern","What is the explicit promise construction antipattern?","Wrapping an **already-promise-returning** operation in `new Promise` is redundant,\nverbose, and easy to get wrong (forgetting to reject, swallowing errors). Just\nreturn\u002Fawait the existing promise.\n\n```js\n\u002F\u002F antipattern — wrapping a promise in a promise\nfunction load() {\n  return new Promise((resolve, reject) => {\n    fetch('\u002Fx').then(resolve).catch(reject)\n  })\n}\n\u002F\u002F just return it\nfunction load() { return fetch('\u002Fx') }\n```\n\nUse `new Promise` **only** to wrap a **callback-based** API that isn't already\npromisified.\n",{"id":156,"difficulty":35,"q":157,"a":158},"async-vs-then-style","When should you use async\u002Fawait vs .then chains?","They're interchangeable, but `async\u002Fawait` usually reads better, especially with\nbranching, loops, and try\u002Fcatch error handling. `.then` chains shine for simple\nlinear transformations and point-free style.\n\n```js\n\u002F\u002F await: clearer with logic between steps\nasync function load() {\n  const user = await getUser()\n  if (!user.active) return null\n  return getPosts(user.id)\n}\n\u002F\u002F then: fine for a straight pipeline\nconst upper = () => fetch('\u002Fx').then(r => r.text()).then(t => t.toUpperCase())\n```\n\nMixing is fine. Prefer `await` for readability; just remember every `await`\nintroduces a microtask boundary.\n",{"id":160,"difficulty":43,"q":161,"a":162},"promise-immutable","Can a promise change its state after settling?","No. A promise transitions **once** from `pending` to either `fulfilled` or\n`rejected`, and after that its state and value are **immutable**. Calling\n`resolve`\u002F`reject` again does nothing.\n\n```js\nconst p = new Promise((resolve, reject) => {\n  resolve('first')\n  resolve('second') \u002F\u002F ignored\n  reject('error')   \u002F\u002F ignored\n})\np.then(v => console.log(v)) \u002F\u002F 'first'\n```\n\nThis guarantee is what makes promises composable: once you have a settled\npromise, its result never changes, and adding more `.then`s later still gives you\nthat same value.\n",{"id":164,"difficulty":35,"q":165,"a":166},"promise-all-empty","What does Promise.all([]) resolve to?","`Promise.all([])` resolves **immediately** with an **empty array** — there's\nnothing to wait for. The same edge case applies to `allSettled([])` (empty\narray). But `Promise.any([])` **rejects** with an `AggregateError` (nothing can\nfulfill), and `Promise.race([])` stays **pending forever**.\n\n```js\nawait Promise.all([])      \u002F\u002F []\nawait Promise.allSettled([]) \u002F\u002F []\nawait Promise.race([])     \u002F\u002F never settles\n```\n\nThese empty-array behaviors are classic gotchas — guard against an empty input\nlist if it could occur, especially for `race`\u002F`any`.\n",{"id":168,"difficulty":35,"q":169,"a":170},"partial-failures","How do you handle partial failures across multiple async tasks?","Use **`Promise.allSettled`**, which waits for every task and reports each\noutcome, so one failure doesn't discard the successes (as `Promise.all` would).\n\n```js\nconst results = await Promise.allSettled(urls.map(fetch))\nconst ok = results.filter(r => r.status === 'fulfilled').map(r => r.value)\nconst failed = results.filter(r => r.status === 'rejected').map(r => r.reason)\n```\n\nThis is the right tool for dashboards, batch jobs, and \"best-effort\" operations\nwhere you want all results regardless of individual failures.\n",{"id":172,"difficulty":35,"q":173,"a":174},"fire-and-forget","What are the risks of \"fire and forget\" promises?","Calling an async function without awaiting or attaching `.catch` (\"floating\npromise\") means errors become **unhandled rejections**, and you lose control over\nordering and completion.\n\n```js\n\u002F\u002F floating — errors vanish, no way to know when it's done\nsaveAnalytics(data)\n\n\u002F\u002F at least handle errors\nsaveAnalytics(data).catch(err => log(err))\n\u002F\u002F or await if the result\u002Fordering matters\nawait saveAnalytics(data)\n```\n\nIf you intentionally don't await, **always** attach a `.catch`. Linters\n(`no-floating-promises`) flag these because silent failures are hard to debug.\n",{"id":176,"difficulty":35,"q":177,"a":178},"await-error-multiple","How do you handle errors across multiple awaits?","A single `try\u002Fcatch` around the awaits catches a rejection from **any** of them —\nexecution jumps to `catch` at the first failure, skipping the rest.\n\n```js\ntry {\n  const user = await getUser()\n  const posts = await getPosts(user.id)\n  const stats = await getStats(posts)\n} catch (err) {\n  \u002F\u002F catches whichever await rejected first\n  console.error(err)\n}\n```\n\nFor **independent** operations where you want all errors (not just the first),\nuse `Promise.allSettled` and inspect each result instead of letting the first\nrejection short-circuit.\n",null,{"description":32},"JavaScript promise and async\u002Fawait interview questions — states, chaining, error handling, Promise.all vs race, and converting callbacks.","javascript\u002Fasync\u002Fpromises","Promises & async\u002Fawait","2026-06-17","qvoFc9byIt-vpMOLYR4_aXHuFSUm0hmc9QGBW5Vegyw",{"id":187,"title":188,"body":189,"description":32,"difficulty":64,"extension":36,"framework":10,"frameworkSlug":8,"meta":193,"navigation":38,"order":33,"path":194,"questions":195,"related":179,"seo":328,"seoDescription":329,"stem":330,"subtopic":331,"topic":19,"topicSlug":21,"updated":184,"__hash__":332},"qa\u002Fjavascript\u002Fasync\u002Fevent-loop.md","Event Loop",{"type":29,"value":190,"toc":191},[],{"title":32,"searchDepth":33,"depth":33,"links":192},[],{},"\u002Fjavascript\u002Fasync\u002Fevent-loop",[196,200,204,208,212,216,220,224,228,232,236,240,244,248,252,256,260,264,268,272,276,280,284,288,292,296,300,304,308,312,316,320,324],{"id":197,"difficulty":35,"q":198,"a":199},"what-is-event-loop","What is the event loop?","JavaScript runs on a **single thread** — one call stack, one thing at a time.\nThe event loop is the coordinator that lets that single thread *appear*\nconcurrent: it repeatedly checks \"is the call stack empty?\" and, when it is,\npulls the next queued callback and pushes it onto the stack to run.\n\nThe pieces involved:\n\n- **Call stack** — the function currently executing.\n- **Web\u002FNode APIs** — timers, network, I\u002FO that run *outside* the engine and\n  hand a callback back when done.\n- **Task & microtask queues** — where those finished callbacks wait.\n\n```js\nwhile (true) {\n  runUntilStackEmpty()   \u002F\u002F execute current synchronous work\n  drainMicrotasks()      \u002F\u002F then ALL queued microtasks\n  if (oneMacrotask) run(oneMacrotask) \u002F\u002F then exactly one macrotask, repeat\n}\n```\n\nSo async code never \"interrupts\" running code — it waits until the stack is\nclear, which is why the event loop is the heart of every \"what's the output\norder?\" interview question.\n",{"id":201,"difficulty":43,"q":202,"a":203},"call-stack","What is the call stack?","The call stack is a **LIFO (last-in, first-out)** structure that tracks where\nthe program is in its execution. Calling a function **pushes** a frame (its\nlocal variables and return address); returning **pops** it. JavaScript has\nexactly one call stack, so it executes one frame at a time.\n\n```js\nfunction a() { b() }\nfunction b() { c() }\nfunction c() { throw new Error('boom') }\na()\n\u002F\u002F Stack at the throw (top first): c -> b -> a -> (global)\n\u002F\u002F That ordering is literally what a stack trace prints.\n```\n\nTwo consequences worth naming in an interview: deep\u002Finfinite recursion\noverflows the stack (`Maximum call stack size exceeded`), and because there's\nonly one stack, any long synchronous function **blocks** everything else until\nit returns.\n",{"id":205,"difficulty":64,"q":206,"a":207},"macro-micro","What is the difference between the macrotask and microtask queues?","There are two priority levels of queued work, and the event loop treats them\nvery differently:\n\n- **Macrotasks** (a.k.a. \"tasks\"): `setTimeout`, `setInterval`,\n  `setImmediate`, I\u002FO callbacks, UI events. The loop runs **one** per\n  iteration.\n- **Microtasks**: promise `.then`\u002F`catch`\u002F`finally` callbacks,\n  `queueMicrotask`, `await` continuations, `MutationObserver`.\n\nThe critical rule: after each macrotask (and after the initial synchronous\nscript), the engine **drains the entire microtask queue** — including any\nmicrotasks those microtasks schedule — *before* picking up the next macrotask\nor rendering.\n\n```js\nsetTimeout(() => console.log('macro'), 0)\nPromise.resolve()\n  .then(() => console.log('micro 1'))\n  .then(() => console.log('micro 2'))\n\u002F\u002F micro 1, micro 2, macro\n\u002F\u002F Both microtasks finish before the timer, even with a 0ms delay.\n```\n\nThat \"microtasks always win\" behavior is why a resolved promise consistently\nbeats a `setTimeout(0)`.\n",{"id":209,"difficulty":64,"q":210,"a":211},"ordering","What is the output order of this snippet?","```js\nconsole.log('A')\nsetTimeout(() => console.log('B'))\nPromise.resolve().then(() => console.log('C'))\nconsole.log('D')\n```\n\n**Output: `A D C B`.** Walk it through the way you would on a whiteboard:\n\n1. `console.log('A')` — synchronous, runs now -> **A**.\n2. `setTimeout(...)` — hands the callback to the timer API; it'll be queued as\n   a **macrotask**. Nothing prints yet.\n3. `Promise.resolve().then(...)` — schedules its callback as a **microtask**.\n   Nothing prints yet.\n4. `console.log('D')` — synchronous -> **D**.\n5. Script finishes, stack is empty -> drain **microtasks** -> **C**.\n6. Next macrotask runs -> **B**.\n\nThe takeaway: all synchronous code first, then every microtask, then the next\nmacrotask.\n",{"id":213,"difficulty":35,"q":214,"a":215},"blocking","What happens if you run a long synchronous loop?","It **blocks the single thread**. While the loop runs the call stack never\nempties, so the event loop can't pull anything from the queues: timers don't\nfire on time, promise callbacks wait, clicks\u002Fscroll feel frozen, and the page\ncan't repaint (the dreaded \"page unresponsive\").\n\n```js\nconst end = Date.now() + 3000\nwhile (Date.now() \u003C end) {} \u002F\u002F freezes the tab for 3 full seconds\nconsole.log('finally free')\n```\n\nFixes: break the work into chunks that yield to the loop (`setTimeout`,\n`requestIdleCallback`), or move CPU-heavy work off the main thread into a **Web\nWorker** (browser) \u002F **Worker thread** (Node) so the UI stays responsive.\n",{"id":217,"difficulty":64,"q":218,"a":219},"starvation","Can microtasks starve the event loop?","Yes. Because the engine drains the **whole** microtask queue before moving on,\na microtask that keeps scheduling more microtasks creates a queue that never\nempties — so macrotasks (timers, I\u002FO) and rendering are postponed\nindefinitely. The tab effectively hangs even though \"async\" code is running.\n\n```js\nfunction loop() {\n  queueMicrotask(loop) \u002F\u002F re-queues itself forever -> macrotasks never run\n}\nloop()\nsetTimeout(() => console.log('I will never print'), 0)\n```\n\nTo avoid starvation, yield to the macrotask queue for repeated\u002Flong work\n(`setTimeout`, `MessageChannel`, or `await` a real async boundary) so the loop\ngets a chance to run timers and paint frames between chunks.\n",{"id":221,"difficulty":35,"q":222,"a":223},"queuemicrotask","What is queueMicrotask?","`queueMicrotask(fn)` schedules a callback to run as a **microtask** — after the\ncurrent synchronous code finishes but before the next macrotask. It's the direct\nway to queue a microtask without abusing `Promise.resolve().then()`.\n\n```js\nconsole.log('sync')\nqueueMicrotask(() => console.log('microtask'))\nconsole.log('sync 2')\n\u002F\u002F sync, sync 2, microtask\n```\n\nUse it to defer work to \"just after this task\" while still beating timers — e.g.\nbatching DOM reads or ensuring a callback runs after the current call stack\nunwinds but ASAP.\n",{"id":225,"difficulty":64,"q":226,"a":227},"raf","How does requestAnimationFrame fit into the event loop?","`requestAnimationFrame(cb)` schedules `cb` to run **right before the next\nrepaint**, typically ~60 times per second. It's neither a macrotask nor a\nmicrotask — the browser runs rAF callbacks in a dedicated step of the rendering\npipeline, just before layout\u002Fpaint.\n\n```js\nrequestAnimationFrame(() => {\n  el.style.transform = `translateX(${x}px)` \u002F\u002F applied in sync with the frame\n})\n```\n\nUse it for animations and visual updates so they're synced to the display\nrefresh (no tearing, no wasted frames). Unlike `setTimeout`, it pauses in\nbackground tabs.\n",{"id":229,"difficulty":35,"q":230,"a":231},"settimeout-clamp","Is setTimeout(fn, 0) really zero milliseconds?","No. `setTimeout(fn, 0)` means \"run as soon as possible **as a macrotask**,\" not\ninstantly. The HTML spec also **clamps** nested timeouts to a minimum (~4ms after\n5 levels of nesting), and the callback still waits for the stack to clear and all\nmicrotasks to drain.\n\n```js\nsetTimeout(() => console.log('timer'), 0)\nPromise.resolve().then(() => console.log('microtask'))\n\u002F\u002F microtask runs first — the \"0ms\" timer is still a macrotask\n```\n\nSo `setTimeout(0)` is \"defer to the next task,\" useful for yielding, but never a\nprecise or immediate timer.\n",{"id":233,"difficulty":64,"q":234,"a":235},"node-vs-browser","How does the event loop differ between Node.js and the browser?","The core idea (stack + queues + microtask draining) is the same, but the\nimplementations differ:\n\n- The **browser** loop is defined by the HTML spec and integrates with\n  **rendering** (rAF, paint, layout).\n- **Node** uses **libuv** with distinct **phases** (timers, pending callbacks,\n  poll, check, close) and adds `process.nextTick` and `setImmediate`, with no\n  rendering step.\n\n```js\n\u002F\u002F Node-only ordering nuance:\nsetImmediate(() => console.log('immediate'))\nsetTimeout(() => console.log('timeout'), 0)\nprocess.nextTick(() => console.log('nextTick'))\n\u002F\u002F nextTick runs before both; immediate vs timeout order can vary\n```\n\nMicrotasks (promises) drain between phases\u002Ftasks in both environments.\n",{"id":237,"difficulty":64,"q":238,"a":239},"nexttick","What is process.nextTick in Node and how does it differ from microtasks?","`process.nextTick(fn)` queues `fn` to run **after the current operation\ncompletes**, before the event loop continues — and crucially **before** the\nPromise microtask queue. So Node effectively has two micro-queues, with\n`nextTick` having higher priority.\n\n```js\nPromise.resolve().then(() => console.log('promise'))\nprocess.nextTick(() => console.log('nextTick'))\n\u002F\u002F nextTick, promise\n```\n\nBecause it preempts everything, recursively scheduling `nextTick` can **starve**\nthe loop entirely. Prefer `queueMicrotask`\u002Fpromises unless you specifically need\nits priority.\n",{"id":241,"difficulty":64,"q":242,"a":243},"await-puzzle","What is the output order of this async\u002Fawait snippet?","```js\nconsole.log('1')\nasync function f() {\n  console.log('2')\n  await null\n  console.log('3')\n}\nf()\nconsole.log('4')\n\u002F\u002F Output: 1, 2, 4, 3\n```\n\n`f()` runs synchronously up to the `await` (logs **2**). `await` suspends the\nfunction and schedules the rest (`console.log('3')`) as a **microtask**, so\ncontrol returns to the caller and **4** logs. After the synchronous code, the\nmicrotask runs -> **3**. The body before the first `await` is synchronous; only\nwhat follows is deferred.\n",{"id":245,"difficulty":64,"q":246,"a":247},"rendering-tasks","When does the browser render relative to tasks and microtasks?","The browser tries to render at most **once per frame** (~16.7ms at 60fps), and it\ndoes so **after** the current macrotask **and** its full microtask queue have\ncompleted — never in the middle of a task.\n\n```\nmacrotask -> drain microtasks -> (rAF callbacks) -> style\u002Flayout\u002Fpaint -> next macrotask\n```\n\nConsequence: synchronous DOM changes within one task aren't painted individually\n— only the final state shows. And a long task blocks rendering entirely, which is\nwhy long synchronous work freezes the page.\n",{"id":249,"difficulty":64,"q":250,"a":251},"await-scheduling","How does await schedule the rest of an async function?","`await x` pauses the function and registers the **remaining code as a\ncontinuation** (a `.then` callback on `x`), which runs as a **microtask** once\n`x` settles. The async function is essentially rewritten into promise\ncontinuations by the engine.\n\n```js\nasync function g() {\n  const a = await fetchA() \u002F\u002F everything below becomes a microtask continuation\n  const b = await fetchB() \u002F\u002F ...and so does this, after a resolves\n  return a + b\n}\n```\n\nSo each `await` introduces a microtask boundary. This is why code after `await`\nalways runs after the current synchronous task, never inline.\n",{"id":253,"difficulty":35,"q":254,"a":255},"settimeout-recursion","What is the difference between setInterval and recursive setTimeout?","`setInterval` queues the callback every N ms **regardless of how long it takes**\n— if the callback is slow, executions can pile up or overlap conceptually.\n**Recursive `setTimeout`** schedules the next run only **after** the current one\nfinishes, guaranteeing a gap.\n\n```js\n\u002F\u002F guarantees ≥1s between completions\nfunction poll() {\n  doWork()\n  setTimeout(poll, 1000)\n}\nsetTimeout(poll, 1000)\n```\n\nRecursive `setTimeout` is preferred for variable-duration work and for adapting\nthe delay dynamically; `setInterval` is fine for short, fixed-cadence ticks.\n",{"id":257,"difficulty":64,"q":258,"a":259},"promise-chain-order","How are chained .then callbacks ordered against each other?","Each `.then` schedules its callback as a **separate microtask**, queued only when\nthe previous link resolves. So two independent chains **interleave** one\nmicrotask at a time.\n\n```js\nPromise.resolve().then(() => console.log('A1')).then(() => console.log('A2'))\nPromise.resolve().then(() => console.log('B1')).then(() => console.log('B2'))\n\u002F\u002F A1, B1, A2, B2\n```\n\n`A1` and `B1` are queued first; running `A1` queues `A2`, running `B1` queues\n`B2`. The engine drains the queue in FIFO order, producing the interleaving.\n",{"id":261,"difficulty":35,"q":262,"a":263},"yield-main-thread","How do you yield to the event loop to keep the UI responsive?","Break long synchronous work into chunks and **schedule the next chunk as a\nmacrotask** so the loop can run timers, handle input, and paint in between.\n\n```js\nfunction processChunk(i) {\n  const end = Math.min(i + 1000, data.length)\n  for (; i \u003C end; i++) handle(data[i])\n  if (i \u003C data.length) setTimeout(() => processChunk(i), 0) \u002F\u002F yield\n}\nprocessChunk(0)\n```\n\nModern options include `scheduler.yield()`, `MessageChannel`, or\n`requestIdleCallback`. For pure CPU work, a **Web Worker** keeps the main thread\nfree entirely.\n",{"id":265,"difficulty":64,"q":266,"a":267},"node-phases","What are the phases of the Node.js event loop?","libuv runs the loop through ordered phases, each with its own callback queue:\n\n1. **timers** — `setTimeout`\u002F`setInterval` callbacks.\n2. **pending callbacks** — deferred I\u002FO callbacks.\n3. **poll** — retrieve new I\u002FO events; execute I\u002FO callbacks.\n4. **check** — `setImmediate` callbacks.\n5. **close** — `close` event callbacks.\n\nBetween **every** phase, Node drains `process.nextTick` and the microtask\n(promise) queues. This phased structure is why `setImmediate` (check) and\n`setTimeout(0)` (timers) can fire in different orders depending on context.\n",{"id":269,"difficulty":64,"q":270,"a":271},"setimmediate","What is the difference between setImmediate and setTimeout(0) in Node?","`setImmediate` runs in the **check** phase, after the **poll** phase, while\n`setTimeout(0)` runs in the **timers** phase at the start of the next loop. Inside\nan **I\u002FO callback**, `setImmediate` is deterministically first; at the top level\ntheir order is non-deterministic.\n\n```js\nconst fs = require('fs')\nfs.readFile(__filename, () => {\n  setTimeout(() => console.log('timeout'), 0)\n  setImmediate(() => console.log('immediate'))\n  \u002F\u002F inside I\u002FO: \"immediate\" always logs first\n})\n```\n\nRule: to run \"after the current I\u002FO, ASAP,\" use `setImmediate`.\n",{"id":273,"difficulty":35,"q":274,"a":275},"promise-executor-sync","Does the Promise executor function run synchronously?","Yes. The function you pass to `new Promise((resolve, reject) => {...})` runs\n**synchronously, immediately**, as part of constructing the promise. Only the\n`.then`\u002F`.catch` **callbacks** are asynchronous (microtasks).\n\n```js\nconsole.log('start')\nnew Promise(resolve => {\n  console.log('executor')   \u002F\u002F runs now, synchronously\n  resolve()\n}).then(() => console.log('then'))\nconsole.log('end')\n\u002F\u002F start, executor, end, then\n```\n\nA common misconception is that everything about a promise is async — the executor\nbody is not.\n",{"id":277,"difficulty":35,"q":278,"a":279},"await-nonpromise","What happens when you await a non-promise value?","`await` wraps any non-promise in `Promise.resolve(value)`, so the value comes back\nas-is — but the function **still suspends for one microtask tick**. So even\n`await 5` defers the rest of the function.\n\n```js\nasync function f() {\n  console.log('before')\n  await 5            \u002F\u002F not a promise, but still yields a microtask\n  console.log('after')\n}\nf()\nconsole.log('sync')\n\u002F\u002F before, sync, after\n```\n\nSo `await` always introduces an async boundary, even for plain values — handy to\nknow for ordering puzzles.\n",{"id":281,"difficulty":35,"q":282,"a":283},"async-not-multithread","Does asynchronous code mean JavaScript is multithreaded?","No. JavaScript runs on a **single thread**; async just means work is **scheduled**\nto run later rather than blocking. The *waiting* (timers, network, file I\u002FO)\nhappens outside the JS engine (in the browser\u002FOS\u002Flibuv), but your callbacks all\nrun one-at-a-time on the same thread.\n\n```js\nsetTimeout(() => console.log('later'), 1000) \u002F\u002F the timer is handled elsewhere;\n\u002F\u002F the callback still runs on the single JS thread when the stack is free\n```\n\nTrue parallelism in JS requires **Web Workers** \u002F **worker threads**, which run\nseparate threads communicating via messages.\n",{"id":285,"difficulty":43,"q":286,"a":287},"why-single-thread","Why is JavaScript single-threaded?","JavaScript was designed single-threaded to keep the **DOM model simple** — if\nmultiple threads could mutate the DOM concurrently, you'd need locks everywhere\nand face race conditions on every UI update. A single thread plus the event loop\ngives concurrency (handling many things) without the complexity of parallelism.\n\n```js\n\u002F\u002F no data races: only one piece of JS touches `count` at a time\nlet count = 0\nbutton.onclick = () => count++\n```\n\nThe trade-off is that CPU-heavy work blocks everything — solved by offloading to\nWeb Workers when needed.\n",{"id":289,"difficulty":43,"q":290,"a":291},"callback-queue","What is the callback (task) queue?","The callback queue (a.k.a. task\u002Fmacrotask queue) holds callbacks that are\n**ready to run** but waiting for the call stack to clear — completed timers,\nresolved I\u002FO, dispatched events. The event loop moves one of them onto the stack\neach iteration.\n\n```js\nbutton.addEventListener('click', handler) \u002F\u002F handler is queued when clicked\nsetTimeout(cb, 100)                        \u002F\u002F cb is queued ~100ms later\n```\n\nIt's strictly FIFO per task source, and the loop only pulls from it when the\nstack is empty and microtasks are drained.\n",{"id":293,"difficulty":64,"q":294,"a":295},"nested-timeout-order","What is the order of nested setTimeout and promise callbacks?","```js\nsetTimeout(() => {\n  console.log('timeout 1')\n  Promise.resolve().then(() => console.log('promise in timeout'))\n}, 0)\nsetTimeout(() => console.log('timeout 2'), 0)\n\u002F\u002F timeout 1, promise in timeout, timeout 2\n```\n\nEach macrotask is followed by a **full microtask drain** before the next\nmacrotask. So after \"timeout 1\" runs, its queued promise microtask executes\n**before** \"timeout 2\". This \"microtasks between every macrotask\" rule is the key\nto these puzzles.\n",{"id":297,"difficulty":64,"q":298,"a":299},"microtask-checkpoint","What is a microtask checkpoint?","A microtask checkpoint is the point where the engine **drains the entire\nmicrotask queue**. It happens after the current macrotask completes (the stack\nempties) and also after certain operations like a callback returning. All pending\nmicrotasks — and any they schedule — run to completion before the next macrotask\nor render.\n\n```js\n\u002F\u002F everything queued via .then\u002FqueueMicrotask runs at the next checkpoint,\n\u002F\u002F before any setTimeout callback\n```\n\nUnderstanding checkpoints explains why microtasks always \"win\" and why they can\nstarve the loop if they keep scheduling more.\n",{"id":301,"difficulty":64,"q":302,"a":303},"ordering-puzzle2","Trace the output of mixed sync, await, promise and setTimeout.","```js\nconsole.log('A')\nsetTimeout(() => console.log('B'), 0)\nPromise.resolve().then(() => console.log('C'))\n;(async () => {\n  console.log('D')\n  await null\n  console.log('E')\n})()\nconsole.log('F')\n\u002F\u002F A, D, F, C, E, B\n```\n\nSync first: **A**, then the async IIFE runs to its `await` -> **D**, then **F**.\nMicrotasks drain next: **C** (queued before the await continuation), then **E**.\nFinally the macrotask: **B**. Order of microtasks follows the order they were\nqueued.\n",{"id":305,"difficulty":35,"q":306,"a":307},"input-responsiveness","How do long tasks hurt responsiveness (INP)?","A \"long task\" (>50ms of synchronous work) monopolizes the single thread, so the\nevent loop can't process the click\u002Fkeypress callback or repaint — the user\nperceives lag. This directly worsens the **INP (Interaction to Next Paint)**\nmetric.\n\n```js\nbutton.onclick = () => {\n  heavyCompute() \u002F\u002F blocks -> the visual response is delayed\n}\n```\n\nFixes: break work into chunks that yield, defer non-urgent work\n(`requestIdleCallback`), debounce, or move computation to a Web Worker so input\nhandling and painting stay snappy.\n",{"id":309,"difficulty":35,"q":310,"a":311},"raf-vs-timeout-animation","Why use requestAnimationFrame instead of setTimeout for animation?","`requestAnimationFrame` runs **in sync with the display refresh** (right before\npaint) and **pauses in background tabs**, so animations are smooth and don't waste\nCPU\u002Fbattery. `setTimeout` fires on an arbitrary schedule unrelated to the frame,\ncausing dropped or doubled frames and jank.\n\n```js\nfunction animate() {\n  move()\n  requestAnimationFrame(animate) \u002F\u002F one update per frame, perfectly timed\n}\nrequestAnimationFrame(animate)\n```\n\nrAF also self-throttles to the screen's refresh rate, whereas a fixed\n`setTimeout(16)` drifts and can't adapt to 120Hz displays.\n",{"id":313,"difficulty":35,"q":314,"a":315},"one-macrotask-per-tick","Why does the loop process only one macrotask per iteration?","Running one macrotask then draining microtasks (and giving rendering a chance)\nkeeps the system **responsive and fair**: it prevents a flood of queued tasks\nfrom blocking input handling and painting, and ensures microtask-based work\n(promises) completes promptly between tasks.\n\n```\n[1 macrotask] -> [all microtasks] -> [maybe render] -> [1 macrotask] -> ...\n```\n\nIf the loop drained the whole macrotask queue at once, a burst of timers\u002Fevents\ncould lock out rendering and user input until they all finished.\n",{"id":317,"difficulty":43,"q":318,"a":319},"sync-vs-async-diff","What is the difference between synchronous and asynchronous code?","**Synchronous** code runs top-to-bottom, each statement **blocking** until it\nfinishes. **Asynchronous** code starts an operation and **continues without\nwaiting**, handling the result later via a callback\u002Fpromise — so the thread isn't\nblocked.\n\n```js\nconst data = readFileSync('f.txt') \u002F\u002F sync: blocks here until done\nreadFile('f.txt', (e, data) => {}) \u002F\u002F async: returns immediately, callback later\n```\n\nAsync is essential in single-threaded JS so that slow I\u002FO (network, disk) doesn't\nfreeze the UI — the event loop schedules the continuation when the result is\nready.\n",{"id":321,"difficulty":64,"q":322,"a":323},"workers-offload","How do Web Workers relate to the event loop?","A Web Worker runs JavaScript on a **separate thread with its own event loop**, so\nCPU-heavy work there doesn't block the main thread's loop (and thus the UI). They\ncommunicate via `postMessage`, which queues a **message-event** (macrotask) on\nthe receiving side.\n\n```js\nconst worker = new Worker('heavy.js')\nworker.postMessage(bigData)\nworker.onmessage = e => render(e.data) \u002F\u002F runs on main thread when free\n```\n\nWorkers don't share memory by default (except `SharedArrayBuffer`), avoiding data\nraces. Use them to keep the main loop free for input and rendering.\n",{"id":325,"difficulty":35,"q":326,"a":327},"defer-with-tasks","How can you defer work to the next tick using the event loop?","To run something **after** the current synchronous code without blocking, schedule\nit on a queue. The choice of queue controls priority:\n\n```js\nqueueMicrotask(fn)        \u002F\u002F ASAP: before the next render\u002Fmacrotask\nPromise.resolve().then(fn) \u002F\u002F also a microtask\nsetTimeout(fn, 0)          \u002F\u002F next macrotask (after microtasks + maybe a paint)\nrequestAnimationFrame(fn)  \u002F\u002F before the next paint\n```\n\nPick a **microtask** when the deferral must beat rendering\u002Ftimers, and a\n**macrotask** when you want to yield the thread (let input\u002Fpaint happen) before\ncontinuing.\n",{"description":32},"JavaScript event loop interview questions — the call stack, task and microtask queues, and how asynchronous code is scheduled.","javascript\u002Fasync\u002Fevent-loop","The Event Loop","v5VF_8JManXxxPa5kLV8PcWEglG8nn9bwl29BN9yPxg",1781808674571]