-
Notifications
You must be signed in to change notification settings - Fork 5
Closed
Labels
Description
Info
difficulty: easy
title: race()
type: question
template: javascript
tags: javascriptQuestion
Implement an async function helper race() that executes multiple asynchronous functions concurrently and returns the result of the first function to complete, similar to Promise.race(). Unlike parallel(), it finishes as soon as any function completes or encounters an error.
Requirements
- Concurrent execution – Execute all async functions simultaneously
- First completion wins – Return the result of the first function to complete (success or error)
- Early termination – Stop and ignore remaining functions once first result is received
- Callback interface – Each async function follows the Node.js callback pattern
(error, data) => void - Return async function –
race()should return a new async function that can be called
Key Behaviors
- Parallel execution – All functions start simultaneously and run concurrently
- First-wins semantics – Return the first result (success or error) and ignore the rest
- Immediate completion – Call final callback as soon as any function completes
- No result collection – Don't wait for or collect results from other functions
- Flexible input – Accept any number of async functions in the array
Example
const async1 = (callback) => {
setTimeout(() => callback(null, 1), 300);
};
const async2 = (callback) => {
setTimeout(() => callback(null, 2), 100);
};
const async3 = (callback) => {
setTimeout(() => callback(null, 3), 200);
};
const first = race([async1, async2, async3]);
first((error, data) => {
console.log(data); // 2, since async2 completes first
});
// Error handling
const asyncFail = (callback) => {
setTimeout(() => callback(new Error('Something failed')), 50);
};
const raceWithError = race([async1, asyncFail, async3]);
raceWithError((error, data) => {
console.log(error.message); // "Something failed"
console.log(data); // undefined
});Key Challenge
The function must coordinate multiple concurrent async operations and return the first result (success or error) while ignoring all subsequent completions.
Template (JavaScript)
javascript.template.md
export function race(asyncFuncs) {
// TODO: Implement me
}import { race } from './index';
describe('race', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
it('should return the first function to complete', (done) => {
const async1 = (callback) => {
setTimeout(() => callback(null, 1), 300);
};
const async2 = (callback) => {
setTimeout(() => callback(null, 2), 100);
};
const async3 = (callback) => {
setTimeout(() => callback(null, 3), 200);
};
const first = race([async1, async2, async3]);
first((error, data) => {
expect(error).toBeNull();
expect(data).toBe(2); // async2 completes first
done();
});
jest.advanceTimersByTime(150);
});
it('should handle empty array', (done) => {
const emptyRace = race([]);
emptyRace((error, data) => {
expect(error).toBeNull();
expect(data).toBeUndefined();
done();
});
});
it('should handle single async function', (done) => {
const asyncSingle = (callback) => {
setTimeout(() => callback(null, 'single'), 100);
};
const singleRace = race([asyncSingle]);
singleRace((error, data) => {
expect(error).toBeNull();
expect(data).toBe('single');
done();
});
jest.advanceTimersByTime(100);
});
it('should return first error and ignore subsequent results', (done) => {
const async1 = (callback) => {
setTimeout(() => callback(null, 1), 100);
};
const asyncFail = (callback) => {
setTimeout(() => callback(new Error('First error')), 50);
};
const asyncSuccess = (callback) => {
setTimeout(() => callback(null, 3), 75);
};
const raceWithError = race([async1, asyncFail, asyncSuccess]);
raceWithError((error, data) => {
expect(error.message).toBe('First error');
expect(data).toBeUndefined();
done();
});
jest.advanceTimersByTime(100);
});
it('should handle functions with same completion time', (done) => {
const async1 = (callback) => {
setTimeout(() => callback(null, 1), 100);
};
const async2 = (callback) => {
setTimeout(() => callback(null, 2), 100);
};
const async3 = (callback) => {
setTimeout(() => callback(null, 3), 100);
};
const sameTimeRace = race([async1, async2, async3]);
sameTimeRace((error, data) => {
expect(error).toBeNull();
// Any of the three values could be returned
expect([1, 2, 3]).toContain(data);
done();
});
jest.advanceTimersByTime(150);
});
it('should handle functions with different completion times', (done) => {
const fastAsync = (callback) => {
setTimeout(() => callback(null, 'fast'), 10);
};
const slowAsync = (callback) => {
setTimeout(() => callback(null, 'slow'), 200);
};
const mediumAsync = (callback) => {
setTimeout(() => callback(null, 'medium'), 100);
};
const mixedRace = race([fastAsync, slowAsync, mediumAsync]);
mixedRace((error, data) => {
expect(error).toBeNull();
expect(data).toBe('fast'); // Fastest completes first
done();
});
jest.advanceTimersByTime(50);
});
it('should handle mixed data types', (done) => {
const asyncString = (callback) => {
setTimeout(() => callback(null, 'hello'), 50);
};
const asyncNumber = (callback) => {
setTimeout(() => callback(null, 42), 100);
};
const asyncBoolean = (callback) => {
setTimeout(() => callback(null, true), 150);
};
const mixedTypesRace = race([asyncString, asyncNumber, asyncBoolean]);
mixedTypesRace((error, data) => {
expect(error).toBeNull();
expect(data).toBe('hello'); // String completes first
done();
});
jest.advanceTimersByTime(100);
});
it('should handle immediate error', (done) => {
const asyncFail = (callback) => {
setTimeout(() => callback(new Error('Immediate error')), 10);
};
const asyncSuccess = (callback) => {
setTimeout(() => callback(null, 'success'), 50);
};
const immediateErrorRace = race([asyncFail, asyncSuccess]);
immediateErrorRace((error, data) => {
expect(error.message).toBe('Immediate error');
expect(data).toBeUndefined();
done();
});
jest.advanceTimersByTime(100);
});
it('should ignore results from slower functions', (done) => {
const fastAsync = jest.fn((callback) => {
setTimeout(() => callback(null, 'fast'), 50);
});
const slowAsync = jest.fn((callback) => {
setTimeout(() => callback(null, 'slow'), 200);
});
const raceTest = race([fastAsync, slowAsync]);
raceTest((error, data) => {
expect(error).toBeNull();
expect(data).toBe('fast');
// Verify that slow function was called but its result is ignored
expect(fastAsync).toHaveBeenCalled();
expect(slowAsync).toHaveBeenCalled();
done();
});
jest.advanceTimersByTime(100);
});
});Template (TypeScript)
typescript.template.md
type Callback = (error: Error | null, data: any) => void;
type AsyncFunc = (callback: Callback, data?: any) => void;
export function race(asyncFuncs: AsyncFunc[]): AsyncFunc {
// TODO: Implement me
}import { race } from './index';
describe('race', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
it('should return the first function to complete', (done) => {
const async1 = (callback: any) => {
setTimeout(() => callback(null, 1), 300);
};
const async2 = (callback: any) => {
setTimeout(() => callback(null, 2), 100);
};
const async3 = (callback: any) => {
setTimeout(() => callback(null, 3), 200);
};
const first = race([async1, async2, async3]);
first((error, data) => {
expect(error).toBeNull();
expect(data).toBe(2); // async2 completes first
done();
});
jest.advanceTimersByTime(150);
});
it('should handle empty array', (done) => {
const emptyRace = race([]);
emptyRace((error, data) => {
expect(error).toBeNull();
expect(data).toBeUndefined();
done();
});
});
it('should handle single async function', (done) => {
const asyncSingle = (callback: any) => {
setTimeout(() => callback(null, 'single'), 100);
};
const singleRace = race([asyncSingle]);
singleRace((error, data) => {
expect(error).toBeNull();
expect(data).toBe('single');
done();
});
jest.advanceTimersByTime(100);
});
it('should return first error and ignore subsequent results', (done) => {
const async1 = (callback: any) => {
setTimeout(() => callback(null, 1), 100);
};
const asyncFail = (callback: any) => {
setTimeout(() => callback(new Error('First error')), 50);
};
const asyncSuccess = (callback: any) => {
setTimeout(() => callback(null, 3), 75);
};
const raceWithError = race([async1, asyncFail, asyncSuccess]);
raceWithError((error, data) => {
expect(error.message).toBe('First error');
expect(data).toBeUndefined();
done();
});
jest.advanceTimersByTime(100);
});
it('should handle functions with same completion time', (done) => {
const async1 = (callback: any) => {
setTimeout(() => callback(null, 1), 100);
};
const async2 = (callback: any) => {
setTimeout(() => callback(null, 2), 100);
};
const async3 = (callback: any) => {
setTimeout(() => callback(null, 3), 100);
};
const sameTimeRace = race([async1, async2, async3]);
sameTimeRace((error, data) => {
expect(error).toBeNull();
// Any of the three values could be returned
expect([1, 2, 3]).toContain(data);
done();
});
jest.advanceTimersByTime(150);
});
it('should handle functions with different completion times', (done) => {
const fastAsync = (callback: any) => {
setTimeout(() => callback(null, 'fast'), 10);
};
const slowAsync = (callback: any) => {
setTimeout(() => callback(null, 'slow'), 200);
};
const mediumAsync = (callback: any) => {
setTimeout(() => callback(null, 'medium'), 100);
};
const mixedRace = race([fastAsync, slowAsync, mediumAsync]);
mixedRace((error, data) => {
expect(error).toBeNull();
expect(data).toBe('fast'); // Fastest completes first
done();
});
jest.advanceTimersByTime(50);
});
it('should handle mixed data types', (done) => {
const asyncString = (callback: any) => {
setTimeout(() => callback(null, 'hello'), 50);
};
const asyncNumber = (callback: any) => {
setTimeout(() => callback(null, 42), 100);
};
const asyncBoolean = (callback: any) => {
setTimeout(() => callback(null, true), 150);
};
const mixedTypesRace = race([asyncString, asyncNumber, asyncBoolean]);
mixedTypesRace((error, data) => {
expect(error).toBeNull();
expect(data).toBe('hello'); // String completes first
done();
});
jest.advanceTimersByTime(100);
});
it('should handle immediate error', (done) => {
const asyncFail = (callback: any) => {
setTimeout(() => callback(new Error('Immediate error')), 10);
};
const asyncSuccess = (callback: any) => {
setTimeout(() => callback(null, 'success'), 50);
};
const immediateErrorRace = race([asyncFail, asyncSuccess]);
immediateErrorRace((error, data) => {
expect(error.message).toBe('Immediate error');
expect(data).toBeUndefined();
done();
});
jest.advanceTimersByTime(100);
});
it('should ignore results from slower functions', (done) => {
const fastAsync = jest.fn((callback: any) => {
setTimeout(() => callback(null, 'fast'), 50);
});
const slowAsync = jest.fn((callback: any) => {
setTimeout(() => callback(null, 'slow'), 200);
});
const raceTest = race([fastAsync, slowAsync]);
raceTest((error, data) => {
expect(error).toBeNull();
expect(data).toBe('fast');
// Verify that slow function was called but its result is ignored
expect(fastAsync).toHaveBeenCalled();
expect(slowAsync).toHaveBeenCalled();
done();
});
jest.advanceTimersByTime(100);
});
});