Suggest an editImprove this articleRefine the answer for “What is the difference between async/await and chaining?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**async/await** - syntactic sugar over Promises that makes async code read top to bottom like sync code. Promise chaining sequences steps with `.then()` calls. ```javascript // Chaining fetch('/api').then(r => r.json()).then(data => console.log(data)).catch(console.error); // Async/await (same result) async function get() { try { const r = await fetch('/api'); console.log(await r.json()); } catch(e) { console.error(e); } } ``` **Key point:** performance is identical; both use the same Promise microtask queue.Shown above the full answer for quick recall.Answer (EN)Image**async/await** - syntactic sugar over Promises that lets you write async code in a straight top-to-bottom flow, while chaining sequences steps through `.then()` calls on the same Promise. ## Theory ### TL;DR - Async/await reads like sync code; chaining reads like a pipeline - Analogy: chaining is a relay race (each runner passes the baton); async/await is one runner pausing at checkpoints - Both compile to identical Promises under the hood, so performance is the same - Use async/await for 2+ sequential steps; chaining for simple one-offs - Error handling: async/await covers everything with one `try/catch`; chaining lets you `.catch()` per step ### Quick example ```javascript // Chaining fetch('https://jsonplaceholder.typicode.com/users/1') .then(res => res.json()) .then(data => console.log(data.name)) // "Leanne Graham" .catch(err => console.error(err)); // Async/await - same result, linear top-to-bottom flow async function getUser() { try { const res = await fetch('https://jsonplaceholder.typicode.com/users/1'); const data = await res.json(); console.log(data.name); // "Leanne Graham" } catch (err) { console.error(err); } } getUser(); ``` The output is identical. The difference is purely in how the code reads. ### Key difference Async/await flattens promise chains into linear code, but at runtime V8 compiles it into a state machine backed by the same Promise microtasks. The practical difference is readability and error handling style. `try/catch` in async/await catches all errors in one block. `.catch()` in chaining attaches per promise when you need finer control at individual steps. ### When to use - 2+ sequential async steps: async/await (readable at any depth) - Simple one-liner: chaining (no need for an async wrapper function) - Loops or conditions: async/await (`for...of` with `await` works naturally) - Different error handling per step: chaining (individual `.catch()`) - Parallel operations: neither alone, use [Promise.all for concurrent requests](/questions/promise-all-allsettled-race) ### Comparison table | Aspect | Async/Await | Chaining (.then()) | |---|---|---| | Readability | Linear, sync-like | Nested for deep sequences | | Error handling | Single `try/catch` | Per-promise `.catch()` | | Performance | Identical | Identical | | Browser support | ES2017+ (99% global) | ES6+ (100% global) | | Debugging | Stack trace pauses at `await` line | Stack trace shows full chain | | Best for | Multi-step logic, React effects, API flows | Quick utils, simple one-offs | ### How it compiles V8 transforms async functions into a state machine. Each `await` yields control via `Promise.then()`, which resumes the function when the promise resolves. Both paths enqueue microtasks on the same [event loop queue](/questions/yak-pratsyuye-event-loop-v-javascript). There is no runtime difference between the two styles. ### Common mistakes **Missing `await` in a loop** ```javascript // Wrong - data races, unpredictable order async function badLoop() { const promises = [fetch('/1'), fetch('/2')]; for (const p of promises) { const data = p.json(); // Missing await! console.log(await data); } } // Correct - sequential and predictable async function goodLoop() { const urls = ['/1', '/2']; for (const url of urls) { const res = await fetch(url); const data = await res.json(); console.log(data); // item 1, then item 2 } } ``` **No `try/catch` in async function** ```javascript async function noCatch() { await fetch('/fail'); // UnhandledRejectionWarning in Node.js 15+ } // Node.js v15+ terminates the process on unhandled rejections. // Wrap in try/catch or add .catch() on the caller. ``` **Awaiting in series when parallel is possible** ```javascript // Slow - waits for first fetch before starting second const d1 = await fetch('/1'); const d2 = await fetch('/2'); // Fast - both start at the same time const [d1, d2] = await Promise.all([fetch('/1'), fetch('/2')]); ``` **Using `await` outside an `async` function** ```javascript function notAsync() { const data = await fetch('/data'); // SyntaxError } // Fix: add async to the function declaration. ``` ### Real-world usage In most Express codebases I've worked in, teams move fully to async/await once they hit more than two sequential DB calls per handler - chaining gets hard to scan fast at that point. - React: async IIFE inside `useEffect` for data fetching - Express: `async (req, res) => {}` route handlers, standard since Node 7.6 - Next.js: server components and API routes use async/await by default - Axios: returns Promises, works naturally with both styles - Puppeteer: `await page.goto()` and `await page.click()` throughout ### Follow-up questions **Q:** What happens if you `await` a non-promise value like `await 42`? **A:** It returns `42` immediately. Non-thenable values auto-wrap as resolved Promises. No actual pause happens. **Q:** Can you use async/await in a browser console? **A:** Yes, but wrap it in an IIFE: `(async () => { const res = await fetch(...); })()`. Top-level await only works in ES2022 modules. **Q:** How do you run two async operations in parallel with async/await? **A:** Start both Promises before awaiting either, then collect with `Promise.all([p1, p2])`. Awaiting them one by one forces serial execution. **Q:** What happens to unhandled rejections in Node.js v15+? **A:** The process terminates. Earlier versions only printed a warning. Always handle rejections with `try/catch` or `.catch()`. **Q:** Why does async/await not improve performance over chaining? **A:** Both enqueue microtasks on the same event loop. V8 compiles async functions to promise chains internally, so the execution model is identical. Only the syntax differs. ## Examples ### Basic: same API call, two styles ```javascript // Chaining - pipeline style fetch('https://jsonplaceholder.typicode.com/posts/1') .then(res => res.json()) .then(post => console.log(post.title)) // "sunt aut facere..." .catch(err => console.error(err)); // Async/await - sequential style async function getPost() { try { const res = await fetch('https://jsonplaceholder.typicode.com/posts/1'); const post = await res.json(); console.log(post.title); // "sunt aut facere..." } catch (err) { console.error(err); } } getPost(); ``` Both log the same title. As the number of steps grows, chaining nests deeper. Async/await stays flat. ### Intermediate: Express route handler ```javascript // Three sequential async steps in one handler app.get('/profile/:id', async (req, res) => { try { const response = await fetch(`https://api.github.com/users/${req.params.id}`); const userData = await response.json(); await db.saveProfile(userData); // save to database res.json(userData); } catch (err) { res.status(500).json({ error: err.message }); } }); ``` With chaining this would be three nested `.then()` calls and a `.catch()` at the end. The async/await version is easier to extend later, such as adding validation or logging between steps.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.