Promises - How to make asynchronous code execute synchronous without async / await?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
In JavaScript, asynchronous operations cannot be made truly synchronous without blocking the event loop, and blocking is usually the wrong choice for application code. The practical goal is sequential execution with clear control flow. You can achieve that with promise chaining, even when async and await are not used.
Run Tasks Sequentially with Promise Chains
A common pattern is to reduce an array of steps into a single promise chain. Each step starts only after the previous one resolves.
This gives sequential behavior without pretending the code is synchronous.
Sequence Dynamic Workloads
If the number of tasks is dynamic, wrap sequencing in a helper. This keeps call sites clean and easy to test.
A helper like this makes ordering guarantees explicit in your API.
Error Handling and Cleanup
Promise chains should include centralized error handling and optional cleanup steps. If one step fails, later steps should not run unless you intentionally recover.
This structure keeps failure behavior predictable in production workflows.
Refactor Callback Workflows into Promise Pipelines
Many requests to make async code synchronous come from callback-heavy legacy code. The practical refactor is to wrap callback APIs in promises and then chain them sequentially. This improves readability and error propagation without changing runtime semantics.
This pipeline runs in order, surfaces failures correctly, and avoids nested callback pyramids. It also creates a clear path to adopting async and await later if your style guide allows it.
Use Queues for User-Triggered Sequential Tasks
When users trigger actions rapidly, queue tasks instead of starting all operations at once. A promise queue guarantees order and prevents resource contention in clients and services.
This pattern gives synchronous-like sequencing while preserving non-blocking execution.
Prefer one sequencing style per module so asynchronous control flow stays easy to read.
Common Pitfalls
- Trying to block the event loop to force sync behavior.
- Launching all promises with
Promise.allwhen order is required. - Forgetting to return inner promises in
.thencallbacks. - Mixing callbacks and promises in ways that hide errors.
Summary
- JavaScript cannot safely make async I O truly synchronous in normal app code.
- Promise chains provide deterministic sequential execution.
- Encapsulate chaining in helpers for reuse and testing.
- Centralize error handling and cleanup for reliable pipelines.
Related reading
- Proper handling of context data in libaio callbacks?
- Proper request with async/await in Node.JS
- Proper use of mutexes in Python
- Proper way of getting several scripts asynchronously using Jquery with post-document-ready callback
- Properly close mongoose's connection once you're done
- Properties order in Margin
- Proper way of handling exception in task continuewith
- Proper way to cache results TaskT with IMemoryCache
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.