Hone logo
Hone
Problems

JavaScript Async Series Executor

Asynchronous operations are fundamental in modern JavaScript development, especially for tasks like network requests, file I/O, and timers. Often, you need to execute these asynchronous operations in a specific sequence, ensuring that one completes before the next one begins. This challenge focuses on building a robust mechanism to manage and execute a series of asynchronous functions in order.

Problem Description

Your task is to implement a JavaScript function, let's call it seriesExecute, that takes an array of asynchronous functions (each returning a Promise) as input. This function should execute these asynchronous functions one after another, in the order they appear in the array. The seriesExecute function itself should return a Promise that resolves with an array of all the results from the executed functions, in the same order. If any of the asynchronous functions in the series rejects, the seriesExecute function should immediately reject with the error from that function, and no further functions in the series should be executed.

Key Requirements:

  1. Sequential Execution: Asynchronous functions must be executed strictly one after another. The next function should only start after the previous one has successfully resolved.
  2. Result Aggregation: Collect the resolved values from all successfully executed asynchronous functions.
  3. Ordered Results: The returned array of results must maintain the order of the original asynchronous functions.
  4. Error Handling: If any asynchronous function in the series rejects, the seriesExecute function must reject immediately with the error, and stop further execution of subsequent functions.
  5. Promise-Based: The input functions are expected to return Promises. The seriesExecute function itself must return a Promise.

Expected Behavior:

  • If all asynchronous functions resolve, the returned Promise resolves with an array containing their resolved values in order.
  • If any asynchronous function rejects, the returned Promise rejects with the error of the first function that rejected.

Edge Cases to Consider:

  • An empty array of asynchronous functions.
  • Asynchronous functions that take varying amounts of time to resolve.
  • Asynchronous functions that might throw synchronous errors (though ideally they should be wrapped in Promises that reject).

Examples

Example 1:

const asyncFunc1 = () => new Promise(resolve => setTimeout(() => resolve('Result 1'), 50));
const asyncFunc2 = () => new Promise(resolve => setTimeout(() => resolve('Result 2'), 30));
const asyncFunc3 = () => new Promise(resolve => setTimeout(() => resolve('Result 3'), 70));

const tasks = [asyncFunc1, asyncFunc2, asyncFunc3];

seriesExecute(tasks)
  .then(results => {
    console.log(results); // Expected: ['Result 1', 'Result 2', 'Result 3']
  })
  .catch(error => {
    console.error('Error:', error);
  });

Explanation: asyncFunc1 executes, then asyncFunc2, then asyncFunc3. All resolve successfully, and their results are collected in order.

Example 2:

const asyncFuncA = () => new Promise(resolve => setTimeout(() => resolve('Success A'), 40));
const asyncFuncB = () => new Promise((_, reject) => setTimeout(() => reject(new Error('Failed B')), 60));
const asyncFuncC = () => new Promise(resolve => setTimeout(() => resolve('Success C'), 20));

const tasks = [asyncFuncA, asyncFuncB, asyncFuncC];

seriesExecute(tasks)
  .then(results => {
    console.log(results);
  })
  .catch(error => {
    console.error('Error:', error.message); // Expected: Error: Failed B
  });

Explanation: asyncFuncA executes and resolves. asyncFuncB then executes and rejects. The seriesExecute function immediately rejects with the error from asyncFuncB, and asyncFuncC is never executed.

Example 3: Empty Task Array

const tasks = [];

seriesExecute(tasks)
  .then(results => {
    console.log(results); // Expected: []
  })
  .catch(error => {
    console.error('Error:', error);
  });

Explanation: When an empty array is provided, the seriesExecute function should resolve immediately with an empty array, as there are no tasks to execute.

Constraints

  • The input tasks will be an array.
  • Each element in the tasks array will be a function that returns a Promise.
  • The number of tasks can range from 0 to 1000.
  • The execution time for individual asynchronous functions can vary significantly, but assume they will eventually resolve or reject.

Notes

  • Consider using async/await for a more readable implementation, but it's not strictly required.
  • Think about how you can iterate through the array of functions while maintaining the asynchronous nature and sequential execution.
  • The Promise.all method executes promises concurrently, which is not what you want here. You need a method that enforces sequential execution.
  • A reduce method on the array could be a good candidate for this problem.
Loading editor...
javascript