Inversion of Control in JavaScript Callbacks
Passing a callback gives up control. Here is what inversion of control is and how promises fix it.
Inversion of Control in JavaScript Callbacks
Inversion of control is a key problem with callbacks. When you pass a callback to a function, you give up control of when and how it is called. The function might call it multiple times, not at all, or with unexpected arguments.
The Problem
fetchUser(id, (user) => { // you trust fetchUser to: // 1. call this callback exactly once // 2. with the right user // 3. at the right time // 4. not before you are ready render(user); }); `` But what if `fetchUser`: - Calls the callback **twice** (renders twice)? - Calls it **zero times** (renders never, page hangs)? - Calls it **too early** (before other setup is done)? - Calls it with **wrong arguments** (renders undefined)? - Calls it **synchronously** when you expected async? You have **inverted control** to `fetchUser`. You are trusting it to behave correctly. ### How Promises Fix This Promises guarantee: 1. **Once-only**: a promise settles (resolves or rejects) exactly once. 2. **Eventually**: the callback runs after the promise settles, never before. 3. **Asynchronously**: `.then` callbacks always run asynchronously (as microtasks). 4. **Value or error**: the callback receives the resolved value or the rejection reason. ```js fetchUserP(id).then((user) => { // guaranteed to run once, with the right user, asynchronously render(user); }); `` You do not trust `fetchUserP` to call the callback correctly. You trust the **promise spec**. ### Async/Await (Same Guarantee) ```js async function main() { const user = await fetchUserP(id); render(user); // guaranteed once, with the right user } `` Async/await is built on promises, so it has the same guarantees. ### The Takeaway Inversion of control is the core problem with callbacks: you trust the function to call the callback correctly. Promises fix this by guaranteeing once-only settlement, async execution, and value-or-error delivery. You trust the promise spec, not the function.
When you pass a callback to a function, you give up control of when and how it is called. The function might call it multiple times, not at all, too early, or with wrong arguments. You are trusting the function to behave correctly.
Promises guarantee once-only settlement (resolve or reject exactly once), async execution (.then callbacks run as microtasks), and value-or-error delivery. You trust the promise spec, not the function calling the callback.
The function might call the callback multiple times (duplicate effects), zero times (page hangs), too early (before setup is done), with wrong arguments (undefined), or synchronously when you expected async.
Yes. async/await is built on promises, so it has the same guarantees: once-only settlement, async execution, and value-or-error delivery. You trust the promise spec, not the function.
A promise settles (resolves or rejects) exactly once. .then callbacks run asynchronously (as microtasks), never synchronously. The callback receives the resolved value or the rejection reason. These guarantees solve inversion of control.
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

