Coding Challenge Practice - Question 91
Source: Dev.to
Problem Description
Implement the async function helper parallel().
The parallel function should accept an array of async functions, each having the signature func(callback). It must run all functions concurrently, collect their results in the original order, and invoke a final callback exactly once.
Requirements
- If the input array is empty, invoke the callback immediately with
undefinederror and an empty results array. - Store each function’s result at the index corresponding to its position in the input array.
- Count completed tasks and call the final callback only after all tasks finish.
- If any function reports an error, invoke the callback with that error and ignore any further results.
- Preserve result order even when tasks finish out of order.
Implementation
function parallel(funcs) {
// Returns a function that accepts the final callback
return function (callback) {
// Handle empty array case
if (funcs.length === 0) {
callback(undefined, []);
return;
}
const results = [];
let completed = 0;
let hasError = false;
// Run all functions in parallel
funcs.forEach((func, index) => {
func((error, result) => {
// Prevent multiple callbacks after an error has occurred
if (hasError) return;
// Handle error from any function
if (error) {
hasError = true;
callback(error, undefined);
return;
}
// Store result preserving original order
results[index] = result;
completed++;
// When all functions have completed, invoke the final callback
if (completed === funcs.length) {
callback(undefined, results);
}
});
});
};
}
Usage Example
// Example async functions
function asyncTask1(cb) {
setTimeout(() => cb(undefined, 'result1'), 300);
}
function asyncTask2(cb) {
setTimeout(() => cb(undefined, 'result2'), 100);
}
function asyncTaskWithError(cb) {
setTimeout(() => cb(new Error('Oops'), undefined), 200);
}
// Run tasks in parallel
const runParallel = parallel([asyncTask1, asyncTask2]);
runParallel((err, results) => {
if (err) {
console.error('Error:', err);
} else {
console.log('Results:', results); // => ['result1', 'result2']
}
});