Hone logo
Hone
Problems

Asynchronous Task Distribution with Master/Worker Pattern in Jest

This challenge focuses on implementing a simplified master/worker pattern in TypeScript, designed to distribute asynchronous tasks across multiple "workers" managed by a central "master." The goal is to simulate a scenario where a master process divides a workload into smaller units and assigns them to worker processes for parallel execution, ultimately collecting and processing the results. This pattern is commonly used to improve performance in computationally intensive applications.

Problem Description

You are tasked with creating a basic master/worker system using TypeScript and Jest for testing. The system should consist of a Master class and a Worker class.

What needs to be achieved:

  1. Master Class: The Master class should accept an array of tasks (represented as functions that return Promises) and a number of workers. It should distribute these tasks evenly among the workers. The master should then collect the results from each worker and return a single array containing all the results in the order the tasks were originally provided.
  2. Worker Class: The Worker class should receive an array of tasks and execute them sequentially. Each worker should return an array of the results of the tasks it processed.
  3. Asynchronous Execution: All tasks should be executed asynchronously using Promises.
  4. Error Handling: If a task throws an error, the worker should catch it and include the error message in the results array instead of crashing.

Key Requirements:

  • The Master class must manage the distribution of tasks to the Worker instances.
  • The Worker class must execute the assigned tasks and return their results.
  • The master must collect results from all workers and return them in the original task order.
  • The code must be written in TypeScript.
  • The solution must be testable using Jest.

Expected Behavior:

The Master class should return a Promise that resolves to an array of results. The order of the results in the array should match the order of the original tasks. If a task fails, the corresponding element in the result array should be an error message.

Edge Cases to Consider:

  • What happens if the number of tasks is not evenly divisible by the number of workers? The master should distribute the remaining tasks to the first available worker.
  • What happens if a worker encounters an error while executing a task? The worker should catch the error and return an error message in the results array.
  • What happens if the number of workers is zero or negative? The master should throw an error.
  • What happens if the task array is empty? The master should return an empty array.

Examples

Example 1:

Input:
tasks: [() => Promise.resolve(1), () => Promise.resolve(2), () => Promise.resolve(3), () => Promise.resolve(4)]
numWorkers: 2
Output: [1, 2, 3, 4]
Explanation: The tasks are distributed to two workers. Worker 1 receives tasks 1 and 2, and Worker 2 receives tasks 3 and 4. Both workers execute their tasks and return their results. The master collects the results and returns them in the original order.

Example 2:

Input:
tasks: [() => Promise.resolve(1), () => Promise.reject("Error 1"), () => Promise.resolve(3), () => Promise.resolve(4)]
numWorkers: 2
Output: [1, "Error 1", 3, 4]
Explanation: The first task resolves to 1, the second task rejects with "Error 1", the third resolves to 3, and the fourth resolves to 4. The master collects the results, including the error message.

Example 3:

Input:
tasks: [() => Promise.resolve(1), () => Promise.resolve(2), () => Promise.resolve(3)]
numWorkers: 1
Output: [1, 2, 3]
Explanation: All tasks are assigned to a single worker. The worker executes all tasks and returns the results. The master collects the results and returns them in the original order.

Constraints

  • The number of tasks will be between 0 and 100.
  • The number of workers will be between 1 and 10.
  • Each task will be a function that returns a Promise.
  • The execution time of each task will be relatively short (less than 100ms).
  • The code should be reasonably efficient and avoid unnecessary overhead.

Notes

  • Consider using Promise.all or similar techniques to manage the asynchronous execution of tasks.
  • Think about how to handle errors gracefully and provide informative error messages.
  • Focus on creating a clean and well-structured solution that is easy to understand and test.
  • The Jest tests will verify the correctness of the master/worker system, including the order of results and error handling.
  • You don't need to implement complex task scheduling algorithms; a simple round-robin distribution is sufficient.
Loading editor...
typescript