developer

Async/Await vs Promises vs Callbacks: Which One to Use and When

JavaScript has three ways to handle async operations. They all work, but they're not equivalent. Here's when each makes sense and the common mistakes to avoid.

7 min readJanuary 28, 2026Updated March 1, 2026By FreeToolKit TeamFree to read

Frequently Asked Questions

What is the difference between a callback, a Promise, and async/await?+
A callback is a function you pass as an argument to be called when an async operation completes. They work but lead to deeply nested 'callback hell' for sequential async operations. A Promise represents a future value — it's an object that's either pending, fulfilled, or rejected. Promises can be chained with .then() and .catch(), which reads more linearly. Async/await is syntactic sugar on top of Promises — it lets you write asynchronous code that looks synchronous. Await pauses execution of the async function until the Promise resolves. Under the hood, async/await is still Promises. All three ultimately use the JavaScript event loop; they're different syntax for the same mechanism.
What is the most common mistake with async/await?+
Accidentally sequential awaits when operations could run in parallel. If you write: const user = await getUser(); const posts = await getPosts(); — these run sequentially, each waiting for the previous to complete. If they're independent operations, you're wasting time. The correct approach is: const [user, posts] = await Promise.all([getUser(), getPosts()]); — both requests fire simultaneously and you wait for both to complete. A related mistake is not wrapping awaits in try/catch, so rejected Promises become unhandled rejections. Either wrap in try/catch or add a .catch() to the outer async function call.
When should I still use .then() chains instead of async/await?+
Async/await requires the function to be declared async, which isn't always possible. For simple transformations in Promise chains (.then(data => data.json()).then(json => json.results)), a chain can be more concise. For non-function contexts like module top level (now mostly solved by top-level await in ES2022), chains were historically necessary. For stream processing and event emitters where you're not dealing with discrete async operations, callbacks and event handlers are still natural. In React, .then() chains are sometimes cleaner inside event handlers where you want to handle the Promise without using an async function. That said, for most sequential async logic with complex error handling, async/await is cleaner.

🔧 Free Tools Used in This Guide

FT

FreeToolKit Team

FreeToolKit Team

We build free browser tools so you don't have to install anything.

Tags:

javascriptasyncpromisesasync-awaitdeveloper