Lesson 75 of 80
Splitting a big array into fixed-size chunks is one line. Turning those chunks into a rate-limited async pipeline is the part interviews actually care about.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Chunking means cutting one array into a list of smaller arrays of a fixed size. [1,2,3,4,5] chunked by 2 becomes [[1,2],[3,4],[5]]. On its own that's a warm-up exercise — the real reason it shows up in production code is batching: you have 5,000 IDs to fetch, and the API rejects a request over 100 IDs, or your DB connection pool chokes if you fire 5,000 queries at once. Chunk the array, then process one chunk at a time (or a few at a time) instead of the whole thing in one shot.
function chunk(arr, size) {
if (size < 1) throw new RangeError("size must be >= 1");
const out = [];
for (let i = 0; i < arr.length; i += size) {
out.push(arr.slice(i, i + size));
}
return out;
}
chunk([1, 2, 3, 4, 5], 2); // [[1,2],[3,4],[5]]
The trick is the loop step: i += size, not i++. Each iteration jumps to the start of the next chunk, and .slice(i, i + size) grabs that window — slice on the last chunk just returns whatever's left, no bounds check needed.
Now use it to batch async work — say, updating 5,000 user records through an API that caps requests at 100 IDs:
async function batchUpdate(ids, updateFn, batchSize = 100) {
const chunks = chunk(ids, batchSize);
const results = [];
for (const group of chunks) {
// one batch finishes before the next starts — deliberate, not accidental
const res = await updateFn(group);
results.push(...res);
}
return results;
}
for...of with await inside runs the batches sequentially — batch 2 doesn't start until batch 1's promise settles. That's often exactly what you want when the constraint is a hard rate limit or an API that literally rejects concurrent writes to the same resource.
chunks.map(c => updateFn(c)) fires every chunk at once — that's not batching, that's just chunking uselessly before doing the exact thing chunking was meant to prevent. The for...of + await loop is what actually serializes the work.chunk(arr, 0) is an infinite loop waiting to happen. If size isn't guarded, i += 0 never advances and the function hangs. Validate the size argument.Promise.all chunk-internal calls but the API doesn't preserve request order in its response, don't assume results[i] maps back to ids[i] without checking — either request an id-tagged response or sort by index yourself.Debounce and throttle limit how often a function runs over time; chunking-and-batching limits how much data one call handles at once. Both exist for the same reason — protect a downstream system from being asked to do too much, too fast. If you've built a task queue with a concurrency cap, chunking is the same instinct applied to a plain array instead of a stream of async calls.