javascriptadvanced20 minutes

Build a Memoized Async Function Runner with Concurrency Control

Create an advanced utility function in JavaScript that memoizes asynchronous functions and limits their concurrency, optimizing performance and resource usage.

Challenge prompt

Write a function `memoizedAsyncRunner` that accepts an asynchronous function `asyncFn` and a concurrency limit `maxConcurrent` as arguments. This function should return a new function with the following behavior: 1) It memoizes results so that multiple calls with the same arguments return cached promises instead of triggering new executions. 2) It enforces a maximum number of concurrent executions (`maxConcurrent`). If the concurrency limit is reached, additional calls are queued and executed in order once previous calls complete. 3) Supports arguments of any JSON-serializable type, using those as memoization keys. Ensure it handles rejection correctly and does not cache rejected promises. The returned function should preserve the async nature and resolve to the original asyncFn's result. Implement this with maximum efficiency for high-concurrency environments.

Guidance

  • Use a Map to store memoized promises keyed by JSON-stringified arguments.
  • Use a queue to track calls waiting to execute when concurrency limit is exceeded.
  • Ensure errors from the async function clear the cache for the relevant key.
  • Use async/await syntax and carefully manage concurrency counters.

Hints

  • Consider maintaining a 'running count' of active asyncFn calls to check before proceeding.
  • To queue calls, return a Promise that resolves when execution starts and completes.
  • Be careful when JSON-stringifying arguments to handle edge cases like nested objects properly.

Starter code

function memoizedAsyncRunner(asyncFn, maxConcurrent) {
  // Your implementation here
}

// Example usage:
// const memoizedFetch = memoizedAsyncRunner(fetchData, 3);

Expected output

Calling the returned function multiple times with the same arguments should return the cached Promise. No more than maxConcurrent Promises should be running concurrently. Rejected Promises should not be cached, allowing retry.

Core concepts

asynchronous programmingmemoizationconcurrency controlpromise management

Challenge a Friend

Send this duel to someone else and see if they can solve it.