Hone logo
Hone
Problems

React Batching System for Efficient Data Operations

This challenge focuses on building a robust batching system in React using TypeScript. Efficiently handling multiple data operations (like API calls) can significantly improve application performance and user experience by reducing network requests and processing overhead. Your task is to create a reusable component that allows users to define and execute batched operations.

Problem Description

You need to build a React component that acts as a batching system for asynchronous operations. This system should group multiple individual operations together and execute them in batches, either immediately or after a short delay, to optimize performance.

Key Requirements:

  1. Batching Mechanism: The system should collect individual operations and group them into batches.
  2. Operation Definition: Each operation should be a function that returns a Promise.
  3. Batch Execution: Batches should be executed asynchronously. You can choose to execute them immediately or with a debounce/throttle mechanism.
  4. Result Aggregation: The system should collect the results from all executed operations.
  5. Error Handling: Gracefully handle errors that occur during individual operation execution.
  6. Reusability: The batching system should be a generic, reusable component or hook that can be integrated into various parts of an application.
  7. TypeScript Support: The entire solution must be written in TypeScript, ensuring type safety.

Expected Behavior:

  • Users should be able to add operations to the batcher.
  • The batcher should decide when to execute a batch (e.g., when a certain number of operations are added, or after a configurable delay).
  • Upon execution, all operations in the current batch should be triggered.
  • The system should track the status of each operation (pending, resolved, rejected).
  • The final results should be available, along with any errors encountered.

Edge Cases to Consider:

  • Adding an operation while a batch is already executing.
  • Clearing pending operations before they are executed.
  • Handling rapid addition of operations.
  • What happens if an operation within a batch rejects?

Examples

Example 1: Simple Batching with Immediate Execution

Let's assume an immediate execution strategy where a batch is sent as soon as an operation is added.

  • Scenario: Add two API call operations to fetch user data.
  • Input Operations:
    1. () => Promise<string> representing fetching user A.
    2. () => Promise<string> representing fetching user B.
  • Expected Behavior:
    • When the first operation is added, it's put into a batch.
    • When the second operation is added, it's also put into the same batch.
    • Immediately, the batch is triggered.
    • The system executes both promises concurrently.
  • Output:
    • A promise that resolves with an array of results: ['User A Data', 'User B Data'].
    • Alternatively, if an error occurs, it might resolve with an array of mixed results and errors, or reject entirely depending on the chosen error handling strategy.

Example 2: Debounced Batching

Consider a scenario where you want to aggregate many small operations (e.g., marking items as "read") and send them to the server only after a short period of inactivity.

  • Scenario: A user quickly interacts with multiple items, triggering "mark as read" operations.
  • Input Operations:
    • () => Promise<void> for item 1.
    • () => Promise<void> for item 2.
    • () => Promise<void> for item 3.
    • ... (many more operations in quick succession)
  • Batching Configuration: A debounce delay of 500ms.
  • Expected Behavior:
    • Each "mark as read" operation is added.
    • The batcher waits for 500ms of inactivity. If no new operations are added within this window, the current batch is executed.
    • If new operations are added within the 500ms, the timer resets.
    • All operations added within that debounce window are sent in a single batch.
  • Output: A single promise that resolves when the batched operations are complete. The results will be an array of void (or whatever the individual promises resolve to).

Example 3: Error Handling in a Batch

  • Scenario: A batch contains operations where one of them fails.
  • Input Operations:
    1. () => Promise.resolve('Success 1')
    2. () => Promise.reject(new Error('Failed operation'))
    3. () => Promise.resolve('Success 3')
  • Expected Behavior: The batcher executes all operations. The successful ones resolve, and the failed one rejects.
  • Output: Depending on the error handling strategy:
    • Option A (Aggregate Results): A promise that resolves with an array like ['Success 1', Error('Failed operation'), 'Success 3'].
    • Option B (Reject on First Error): The promise for the batch rejects with Error('Failed operation').
    • The challenge asks for a robust system, so aggregating results with detailed error information is preferred.

Constraints

  • The batching system should be implementable as a React Hook (useBatcher).
  • The hook should accept an optional configuration object for batchSize (maximum operations per batch) and debounceDelay (milliseconds to wait before executing a batch if no new operations are added).
  • Operations are defined as functions returning Promise<T>, where T can be any type.
  • The hook should return functions to addOperation, and potentially clearPendingOperations.
  • It should also return the overall status of the batch operations (e.g., isLoading, results, errors).
  • Performance: The system should handle a large number of operations efficiently, aiming to minimize redundant network calls.

Notes

  • Consider using Promise.all or Promise.allSettled for executing operations within a batch, depending on your chosen error handling strategy.
  • A debouncing mechanism is recommended to avoid overwhelming the server with too many small, rapid requests. You'll need to manage timers within your hook.
  • Think about how you will represent the collected results and errors. An array of individual operation results/errors, or a single aggregated result/error for the entire batch, are common patterns.
  • The goal is to create a flexible and performant utility for managing asynchronous tasks in React.
Loading editor...
typescript