Python asyncio version:
import asyncio
class TaskQueue:
def __init__(self, concurrency: int):
self.sem = asyncio.Semaphore(concurrency)
def submit(self, coro) -> asyncio.Task:
async def _run():
async with self.sem:
return await coro
return asyncio.create_task(_run())
TypeScript version (no libraries) — the one interviewers probe harder:
class TaskQueue {
private running = 0;
private waiting: Array<() => void> = [];
constructor(private limit: number) {}
async submit<T>(fn: () => Promise<T>): Promise<T> {
if (this.running >= this.limit) {
await new Promise<void>(res => this.waiting.push(res));
}
this.running++;
try {
return await fn();
} finally {
this.running--;
this.waiting.shift()?.(); // wake exactly one waiter (FIFO)
}
}
}
The graded subtleties: release in finally so failures don't leak slots; wake exactly one waiter; preserve FIFO fairness; a rejected task must reject its own promise without killing the queue.
Follow-ups: Add per-task timeout and cancellation; add drain(). Bounded queue — what happens when submissions outpace completion? (Backpressure: block, drop, or reject.) Priorities; retries with exponential backoff + jitter. The Neon write-up's angle: interviewers now watch how you use an AI assistant on this problem — drive it with tests and invariants, don't paste-and-pray.