{"post":{"slug":"callbacks-vs-promises-vs-async-await-2026","title":"Callbacks vs promises vs async await in 2026: which to use","excerpt":"Callbacks, promises, and async await all solve the same problem: code that has to wait. Here is the same task written three ways, and when to pick each.","category":"code","date":"July 10, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">A</span>ll three of these solve the same underlying problem: running code once a slow operation finishes, without freezing everything else while it waits. By 2026 you will meet all three in real codebases, often in the same file. Callbacks are the oldest and still the foundation everything else is built on. Promises wrap a callback in an object you can chain and pass around. Async await is syntax that lets you write promise-based code as if it were synchronous. None of them is obsolete; each one is still the right tool somewhere.\n\n## What changed in 2026\n\n- **Nearly every core API now has a promise-based version.** Node.js and browser APIs that used to be callback-only mostly ship a promise-based alternative too, such as `fs.promises`, so writing new callback-based I/O code in 2026 is a choice, not a requirement.\n- **Default linting nudges teams toward one style.** Most 2026 starter kits flag unhandled promise rejections and favor async await over raw `.then()` chains.\n- **AI-generated code defaults to async await almost universally.** Callback-based code in a codebase is now more often a sign of age, or of wrapping a callback-only library, than a recent stylistic choice.\n\n## The same task, three ways\n\n```js\n// 1. Callback\nfunction loadUser(id, callback) {\n  fetchUserFromApi(id, (err, user) => {\n    if (err) return callback(err);\n    callback(null, user);\n  });\n}\n\n// 2. Promise\nfunction loadUser(id) {\n  return fetchUserFromApi(id)\n    .then(user => user)\n    .catch(err => { throw err; });\n}\n\n// 3. Async await\nasync function loadUser(id) {\n  try {\n    return await fetchUserFromApi(id);\n  } catch (err) {\n    throw err;\n  }\n}\n```\n\nSame behavior, three shapes. The callback version needs a manual error check on every single call. The promise version centralizes errors in `.catch`. The async await version reads closest to ordinary synchronous code.\n\n## Side-by-side comparison\n\n| | Callbacks | Promises | Async await |\n| --- | --- | --- | --- |\n| Introduced | Always been in JavaScript | ES2015 | ES2017 |\n| Error handling | Manual check on every call | `.catch()` | `try` / `catch` |\n| Reads like synchronous code | No | Somewhat, when chained | Yes |\n| Running tasks in parallel | Manual counting or a library | `Promise.all` | `await Promise.all` |\n| Risk of deep nesting | High, sometimes called callback hell | Low | Very low |\n| Underlying mechanism | Function passed as an argument | Object wrapping a callback | Sugar over promises |\n\n## How they relate to each other\n\nAsync await is not a competitor to promises; it is built directly on top of them. An async function always returns a promise, and `await` unwraps one. Promises, in turn, are typically implemented by wrapping a plain callback-based operation, such as a timer or a network request, once at the boundary, so the rest of the code never has to touch a raw callback again. This is why converting an old callback API usually means writing one small wrapper rather than rewriting everything downstream. That wrapper function holds onto `resolve` and `reject` between calls using [a closure](/blog/what-is-a-closure-in-javascript-2026), the same mechanism that makes private variables work.\n\n## Running things in parallel, three ways\n\n```js\n// Callback: manual counting\nlet done = 0, results = [];\nids.forEach((id, i) => {\n  fetchUserFromApi(id, (err, user) => {\n    results[i] = user;\n    if (++done === ids.length) allDone(results);\n  });\n});\n\n// Promise\nPromise.all(ids.map(id => fetchUserFromApi(id))).then(allDone);\n\n// Async await\nconst results = await Promise.all(ids.map(id => fetchUserFromApi(id)));\n```\n\nThis is where the gap is widest. The callback version is easy to get wrong, miscounting `done` is a classic bug, while both promise-based versions get parallelism for free from `Promise.all`.\n\n## When to pick which\n\nWriting new code today, default to async await; it is the most readable and the current convention. Combining several independent async operations, use `Promise.all` or `Promise.allSettled`. Wrapping an old callback-only library, write one small promise wrapper around it, then use async await everywhere else that calls it. The underlying scheduling stays identical no matter which style sits on top; see [what the event loop is](/blog/what-is-the-event-loop-2026). Inheriting a large callback-based codebase, avoid a big rewrite; wrap and convert file by file as each one is touched anyway.\n\n## FAQ\n\n**Is async await faster than promises or callbacks?**\nNo. It is the same underlying mechanism as promises, which are the same underlying mechanism as callbacks. The differences are readability and error handling, not raw performance.\n\n**Do I still need to learn callbacks if async await exists?**\nYes. Many libraries and browser APIs, event listeners, timers, and streams among them, are still callback-based at their core, and promises themselves are usually built by wrapping one.\n\n**Can I mix async await and then chains in the same codebase?**\nYes, they are fully compatible since async await is built on promises, but pick one style per function so the code stays easy to follow.\n\n**What is callback hell, and does async await fix it?**\nCallback hell is deeply nested callbacks that result from chaining dependent async steps. Async await effectively eliminates it for sequential code, since each step becomes the next line instead of a new nested function.\n\n## Where to go next\n\n- [What is the event loop in 2026](/blog/what-is-the-event-loop-2026)\n- [What is a closure in JavaScript in 2026](/blog/what-is-a-closure-in-javascript-2026)\n- [What is hoisting in JavaScript in 2026](/blog/what-is-hoisting-in-javascript-2026)","hue":280,"metaTitle":"Callbacks vs Promises vs Async Await 2026","metaDescription":"Callbacks vs promises vs async await in 2026: the same task solved three ways, a side-by-side comparison table, and a clear rule for picking one.","keywords":["callbacks vs promises vs async await 2026","async await vs promises","callback vs promise difference","when to use async await","javascript async patterns compared","callback hell example","promise chaining vs async await","async javascript comparison","convert callback to promise","convert promise to async await"],"tldr":["**All three do the same job: run code after something finishes** — they differ in syntax and error handling, not in what is fundamentally possible.","**Callbacks came first and still show up in older APIs and libraries** — promises and async await are usually a wrapper around a callback somewhere underneath.","**Async await is the most readable for sequential steps** — it is a promise chain, written to look like ordinary top-to-bottom code.","**Error handling is the real differentiator** — callbacks need a manual error check every time, promises centralize it in catch, async await allows ordinary try and catch.","**Skip:** rewriting working, well-tested callback-based code to async await for its own sake; migrate the code you are already touching, not code that is not broken."],"readTime":"4 min read","wordCount":888},"related":[{"slug":"what-is-a-promise-chain-2026","title":"What Is a Promise Chain in 2026? Chaining .then Explained","excerpt":"A promise chain links multiple then calls so each step waits for the last one to finish. Here is how chaining actually works, where it breaks, and how it compares to async await in 2026.","category":"code","date":"July 20, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":320},{"slug":"what-is-a-promise-in-javascript-2026","title":"What is a Promise in JavaScript in 2026? Async results explained","excerpt":"A Promise is a JavaScript object that represents a value that is not ready yet, like a network response. This guide explains promise states, then and catch, and how async await fits in.","category":"code","date":"May 25, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":240},{"slug":"sync-vs-async-2026","title":"Sync vs async in 2026: when to use each and why it matters","excerpt":"Synchronous code is simpler; async code handles concurrency. Choosing wrong either blocks your server or creates callback hell. This is the 2026 guide.","category":"code","date":"April 21, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":314}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"byte-pair-encoding-explained-2026","title":"Byte Pair Encoding Explained 2026: How LLM Tokenizers Work","excerpt":"Byte pair encoding is the subword algorithm behind most LLM tokenizers, from GPT models to open-weight releases. Here is how it works and why it still matters in 2026.","category":"ai","date":"July 10, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":150},"older":{"slug":"canary-testing-for-ai-models-2026","title":"Canary testing for AI models in 2026: how it actually works","excerpt":"Canary testing routes a small slice of real traffic to a new model before a full rollout. Here is how teams set thresholds, pick traffic splits, and avoid false alarms in 2026.","category":"ai","date":"July 10, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":150}}