Skip to solution
hardSystem Design

Implement an async task queue with a concurrency limit.

1.1k views
01

Understand the problem

Question presented to candidate: "You have a list of async jobs, say a hundred URLs to fetch, but you cannot run all of them at once without overwhelming the downstream service or the browser's own connection limits. Implement a task queue that runs at most N of them concurrently, starts a new one the instant a slot frees up, and still gives the caller back every result once everything finishes."

What a strong answer should cover:

  • A queue holds pending tasks (functions that return a promise) plus a running count; it only starts a new task when running < concurrency, and starts one MORE the instant a task finishes — not in a fixed batch.
  • 📌 Interview term: a semaphore pattern — the running counter, incremented when a task starts and decremented when it settles, is exactly a semaphore controlling how many tasks may be "in flight" at once.
  • Each call to add a task should return its OWN promise that resolves (or rejects) with that specific task's own outcome — the caller needs its results correlated to its own individual tasks, not just "the batch finished."
  • A rejected task must not stop the queue or block sibling tasks — a real, robust queue isolates failures per task, unlike a naive Promise.all, which stops at the first rejection.
  • A precise answer distinguishes this from two naive alternatives and their real trade-offs: Promise.all (unlimited concurrency, all at once) and a sequential for-loop with await (concurrency of exactly 1, needlessly serial).
  • Results should come back in the CALLER's original order, not completion order, since Promise.all over the returned promises already gives this for free as long as each task's own promise is returned immediately when queued.

Clarifying questions expected:

  • "Should a task be allowed to add MORE tasks to the same queue while it is running?" — affects whether the internal queue array can safely be mutated mid-iteration.
  • "If one task fails, should the whole batch fail fast, or should the caller get every result (success and failure) individually?" — changes whether Promise.all or Promise.allSettled is the right tool for the caller's own aggregation.
  • "Is the concurrency limit fixed for the queue's lifetime, or does it need to change at runtime?"

Code / implementation expected: Yes — a real, runnable implementation, executed with a real running-concurrency counter and a real rejection-isolation test, is the concrete way to prove the limit is actually enforced and failures do not cascade.

promisesconcurrencyqueue
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for JavaScript async/concurrency implementation interviews. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. Every number and log line below is real, captured output from act

Solution ready — 2 min read

Classified // press E to declassify

04

Run the code

JSA real, runnable demo: the TaskQueue implementation, the concurrency-limit proof, and the rejection-isolation proof (identical to this doc's verified output)
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 126 of 165 decoded in the JavaScript track. One more won't hurt.

Back to track