A practice prompt we wrote. No company or candidate report names it, so it carries no company tag.
How to answer
Say the core idea early: calling fetch starts the request, and the promise is only a handle on its result. So the cap must control when fetch is called; building every promise first has already lost.
- Pin the contract.
mapLimit(items, limit, fn, { signal, timeoutMs }), wherefn(item, index, signal)gets anAbortSignal, returns one outcome per input, in input order, shaped likePromiseSettledResult. Ask whether a failure cancels the rest; for a , collect. - Rule out the near misses out loud.
Promise.all(items.map(fn))has no cap. Fixed chunks are capped but slow: each waits for its slowest request. In production you’d use p-map withconcurrency; name it, then write the pool. - Write the worker pattern. Start
Math.min(limit, items.length)async workers. Each loops: takeconst i = next++, awaitfninside atry, store the outcome atout[i]. Thenawait Promise.all(workers). The counter needs no lock, because nothing is awaited between readingnextand incrementing it. Thetrycatches a synchronous throw as well as a rejection, so no worker dies. - Cover the edges. Reject a
limitbelow one or a non-integer, and return[]for no input. Pass each callAbortSignal.any([signal, AbortSignal.timeout(timeoutMs)]), so a hung call cannot hold a slot forever and the caller can stop the run; workers checksignal.abortedbefore taking the next index. - Test with a fake that measures. It counts requests in flight, records the peak, and returns deferreds the test resolves in a chosen order, so a failure reproduces. Assert the peak never exceeds the limit and reaches it when there are at least
limititems, outputs keep input order, and failures sit at their own indexes.
Then say what the cap does not do: it bounds concurrency, not rate. A per-second limit needs a too.
The capped fetch drill runs this with hidden tests on the cap, the order and the failures.
Follow-ups
What the interviewer may ask next, once your first answer is on the table.
- The partner limits requests per second, not requests in flight. What changes in your code?
- One request never settles. What happens to your pool, and how do you bound it?
- The caller wants everything stopped on the first authentication error. How do you cancel the requests still running?
- The inputs arrive as a stream you cannot hold in memory. What changes?
Where answers go wrong
- Building
items.map(fetchOne)and handing the array to a limiter, when every request started the momentmapran. - Running fixed batches through
Promise.all, so one slow request holds up the rest of its batch and the cap is rarely reached. - Letting a rejection escape a worker’s loop, so
Promise.all(workers)rejects on the first failure, the caller loses every result, and the surviving workers keep sending requests nobody reads.
Answer this in two minutes
Write the answer you would say out loud. The clock starts with your first word.
Model answer
“The idea everything hangs on: calling fetch starts the request, and the promise is only a handle on its result. So Promise.all(urls.map(fetch)) has no cap, and neither does handing an array of promises to a limiter, because the requests started when map ran.