FrontendPro

104Implement asyncMap() over an async iterable with bounded concurrency and backpressure

hardAbout 60 minutesCodingasync-iteratorsgeneratorsbackpressurestreams

Problem

Implement an async generator asyncMap(source, mapper, { concurrency = 4, ordered = true } = {}). source is any iterable or async iterable (for example paginated API results or lines of a huge file). It yields await mapper(item, index) for every item.

for await (const thumbnail of asyncMap(readFiles(folder), makeThumbnail, { concurrency: 3 })) {
  gallery.append(thumbnail);
}

Requirements

  • At most concurrency mapper calls run at the same time.
  • Backpressure: pull from source only when there is room. If the consumer stops reading, the generator stops pulling. The number of items pulled but not yet yielded never exceeds concurrency.
  • ordered: true yields in input order; ordered: false yields each result as soon as it is ready.
  • If the consumer stops early (break in for await), close the source (call its return(), so its finally blocks run).
  • If a mapper rejects or the source throws, the generator throws that error.

Try it yourself

Write your solution, run it, then submit to check it against the tests.

solution.jsJavaScript

Press Run or ⌘ Enter to execute your code.

Step-by-step

Use source[Symbol.asyncIterator]?.() or source[Symbol.iterator](). Pulling manually with await iterator.next() is what lets you decide when to read the next item, which is the essence of backpressure.

Unlock the full solution

What's inside

  • Step-by-step
  • Approach
  • Solution
  • Complexity
  • Edge cases
  • Follow-up questions
  • Tests

Includes 2 code listings, 5 edge cases, 4 follow-up questions.

One-time payment by UPI, card or net banking. Launch price.

All 150 answersOne payment, yours forever