Run Promise Tasks with Bounded Concurrency
WaymoFrontend Engineer · OnsiteLast updated
Implement an asynchronous task runner in TypeScript that limits concurrent execution, handles synchronous throws and rejections gracefully, and returns settled results in the original task order.
Problem Statement
In simulation dashboards and autonomous vehicle teleoperation tools, frontend clients often need to fetch dozens of assets or telemetry chunks without overwhelming the browser's network pool or backend endpoints.
Implement a function mapConcurrentSettled that executes an array of asynchronous task factories with a strict upper bound on concurrency.
Requirements
- Validation: If
concurrencyis not an integer or is less than1, reject immediately with aRangeError. - Execution Cap: At any point in time, no more than
concurrencytask factories may be actively executing. - Single Execution: Each task factory must be invoked exactly once.
- No Fail-Fast: Execution must continue until all tasks have settled. A failure in one task must not abort remaining tasks.
- Sync & Async Errors: A task factory that throws synchronously must be captured as a rejection rather than halting the runner.
- Order Preservation: The returned promise resolves to an array of
PromiseSettledResultobjects, ordered to match the originaltasksinput order.
Interface
type PromiseSettledResult =
| { status: 'fulfilled'; value: T }
| { status: 'rejected'; reason: unknown };
type TaskFactory = () => Promise | T;
function mapConcurrentSettled(
concurrency: number,
tasks: TaskFactory[]
): Promise[]>;
Constraints
tasks.lengthis between0and10,000.concurrencyis an integer `1 new Promise((resolve) => setTimeout(() => resolve('A'), 50)), () => Promise.reject(new Error('Network error')), () => { throw new Error('Sync fail'); }, () => 'B' ];
const results = await mapConcurrentSettled(2, tasks); // Output: // [ // { status: 'fulfilled', value: 'A' }, // { status: 'rejected', reason: Error('Network error') }, // { status: 'rejected', reason: Error('Sync fail') }, // { status: 'fulfilled', value: 'B' } // ]
### Example 2
```typescript
await mapConcurrentSettled(0, [() => Promise.resolve(1)]);
// Rejection: RangeError("concurrency must be an integer >= 1")
Edge Cases
- Empty
tasksarray: Resolve immediately to[]. concurrency >= tasks.length: All tasks start immediately.- Task returns a non-promise value (e.g. primitive
T): Must be handled as a resolved promise.
Hints
Hint 1
Pre-allocate an output array of size tasks.length and track a shared nextIndex pointer that worker loops consume.
Hint 2
Wrap task execution inside Promise.resolve().then(task) to catch both synchronous throws and asynchronous promise rejections seamlessly.
Hint 3
Spawn exactly Math.min(concurrency, tasks.length) worker promises that repeatedly pull tasks until the task queue is exhausted.
Follow-up questions
- How would you extend this scheduler to accept an AbortSignal to cancel pending tasks and stop launching new ones?
- How would you write a deterministic unit test to prove concurrency never exceeded k without relying on fragile setTimeout delays?
- How would you modify the API to support prioritized tasks where critical simulation events jump ahead in the queue?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.