Javascript, async, lock?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
JavaScript does not have a built-in mutex like many multithreaded languages, but async race conditions still happen whenever multiple tasks await in the middle of a shared critical section. In those cases, an async lock or mutex is just a queue that guarantees one piece of asynchronous work enters the protected section at a time.
Core Sections
Why a lock can matter in single-threaded JavaScript
JavaScript usually runs one piece of synchronous code at a time, but asynchronous operations can interleave in surprising ways. If two requests both read shared state, await, and then write back, the later completion can overwrite the earlier result.
If incrementSlowly() runs twice concurrently, the final value may still be 1 instead of 2. That is the kind of bug an async lock prevents.
A minimal async mutex
One common approach is to chain promises so each caller waits for the previous one to finish.
Now concurrent calls are serialized through the mutex queue.
What should go inside the lock
Only place the truly shared critical section inside the lock. If you lock too much, unrelated work is forced to wait and throughput drops. If you lock too little, the race remains.
A practical pattern is:
- gather independent inputs outside the lock
- lock only the read-modify-write section
- release as soon as shared state is consistent again
This matters in browser apps, Node.js services, and any code that coordinates writes to memory, caches, or files.
Prefer simpler coordination when possible
Sometimes a lock is not the best fix. Alternatives include:
- making operations idempotent
- using atomic database updates
- serializing through a single worker queue
- avoiding shared mutable state entirely
A mutex is useful, but it should not be the first tool if the underlying design can be simplified.
Libraries can save time
If you need a tested implementation, libraries such as async-mutex provide lock primitives with clearer APIs and fewer edge-case mistakes than ad hoc queue code.
That is often the right choice in production code unless the requirements are extremely small.
Common Pitfalls
- Assuming JavaScript cannot have race conditions because it is single-threaded.
- Locking too much code and accidentally turning unrelated async work into a bottleneck.
- Forgetting to release the lock when an exception occurs, which is why
finallymatters. - Using a lock where a database transaction, queue, or redesign would solve the problem more cleanly.
- Reimplementing a mutex casually in production code without testing cancellation and error paths.
Summary
- JavaScript can still have async race conditions whenever awaited operations interleave around shared state.
- An async lock is usually a promise-based queue that serializes access to a critical section.
- Keep the locked section small and release it reliably with
finally. - Consider simpler architectural alternatives before adding lock logic.
- For production use, a well-tested mutex library is often safer than a homemade implementation.
Related reading
- JavaScript Asynchronous method in while loop
- JavaScript asynchronous race condition
- Javascript background loop
- JavaScript callbacks for asynchronous functions is there any pattern to differentiate between return value and exception?
- JavaScript Calculate the nth root of a number
- Javascript call() & apply() vs bind()?
- javascript class property not set in success function of ajax call
- JavaScript generator-style async
.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.