📚 4 min read
Promise.all Implementation ​
Overview ​
Promise.all()
takes an array of promises and returns a new promise that resolves when all input promises have resolved, or rejects if any promise rejects. This implementation includes performance monitoring and enhanced error handling.
Implementation ​
typescript
import { AsyncOperationError } from '../advanced/error-handling';
import { PerformanceMonitor } from '../advanced/performance-monitoring';
function promiseAll<T>(promises: Array<Promise<T>>): Promise<T[]> {
const monitor = PerformanceMonitor.getInstance();
return monitor.trackOperation(
'Promise.all',
() =>
new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(
new AsyncOperationError(
'Argument must be an array',
'INVALID_ARGUMENT',
'Promise.all'
)
);
}
const results: T[] = new Array(promises.length);
let unresolved = promises.length;
if (unresolved === 0) {
resolve(results);
return;
}
promises.forEach((promise, index) => {
monitor
.trackOperation(`Promise.all.item[${index}]`, () =>
Promise.resolve(promise)
)
.then((value) => {
results[index] = value;
unresolved -= 1;
if (unresolved === 0) {
resolve(results);
}
})
.catch((error) => {
reject(
AsyncOperationError.from(error, `Promise.all.item[${index}]`)
);
});
});
})
);
}
Usage Examples ​
Basic Usage ​
typescript
const promises = [
Promise.resolve(1),
Promise.resolve(2),
new Promise<number>((resolve) => setTimeout(() => resolve(3), 1000)),
];
promiseAll(promises)
.then((results) => {
console.log(results); // [1, 2, 3]
})
.catch((error) => {
console.error('One of the promises rejected:', error);
});
Advanced Usage with Timeout ​
typescript
// With timeout wrapper
function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
const timeout = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('Timeout')), ms);
});
return Promise.race([promise, timeout]);
}
// Usage with timeout
promiseAll([
withTimeout(fetch('api/data1'), 5000),
withTimeout(fetch('api/data2'), 5000),
]).then(([data1, data2]) => {
console.log('Both requests completed within timeout');
});
Error Handling ​
typescript
// Testing different scenarios
async function testPromiseAll() {
try {
// Mixed success and failure
await promiseAll([
Promise.resolve(1),
Promise.reject(new Error('Failed')),
Promise.resolve(3),
]);
} catch (error) {
console.error('Caught rejection:', error);
}
try {
// Empty array
const emptyResult = await promiseAll([]);
console.log('Empty array result:', emptyResult); // []
} catch (error) {
console.error('Should not reach here');
}
}
Key Features ​
Parallel Execution
- All promises run concurrently
- Maintains input order in results
- Optimized for parallel processing
Error Handling
- Fast failure on first rejection
- Detailed error context
- Performance monitoring integration
Input Validation
- Handles non-array inputs
- Supports mixed input types
- Empty array optimization
Resource Management
- Memory-efficient result collection
- Proper cleanup on rejection
- Performance tracking per promise
Edge Cases ​
- Empty array input
- Non-array input
- Mixed input types (promises and values)
- Promises that reject
- Already settled promises
- Non-promise thenables
Best Practices ​
- Use for operations that must all succeed
- Consider
Promise.allSettled()
if you need all results regardless of success/failure - Be mindful of memory usage with large arrays
- Include proper error handling
- Consider adding timeout mechanisms for long-running operations
Common Pitfalls ​
Not Handling Rejections
typescript// Bad: No error handling const results = await Promise.all(promises); // Good: Handle potential rejections try { const results = await Promise.all(promises); } catch (error) { console.error('One or more promises failed:', error); }
Memory Leaks with Large Arrays
typescript// Bad: Loading too many promises into memory const promises = items.map((item) => fetchData(item)); const results = await Promise.all(promises); // Good: Process in batches const batchSize = 100; for (let i = 0; i < items.length; i += batchSize) { const batch = items.slice(i, i + batchSize); const promises = batch.map((item) => fetchData(item)); const results = await Promise.all(promises); }
Mixing Sync and Async Operations
typescript// Bad: Mixing sync and async operations const promises = items.map((item) => { if (item.cached) return item.data; return fetchData(item); }); // Good: Ensure all operations are promises const promises = items.map((item) => { if (item.cached) return Promise.resolve(item.data); return fetchData(item); });
Not Considering Promise Order
typescript// Bad: Assuming results order matches completion order const [slow, fast] = await Promise.all([slowFetch(), fastFetch()]); // Good: Use explicit mapping if order matters const results = await Promise.all(promises); const resultMap = { slow: results[0], fast: results[1], };
Performance Considerations ​
Memory Management
- Results array pre-allocated for efficiency
- Cleanup of intermediate results on rejection
- WeakMap usage for metadata storage
- Batch processing for large arrays
Execution Order
- All promises execute in parallel
- No guaranteed execution order
- Early rejection optimization
- Microtask queue utilization
Resource Usage
- Network connection pooling
- CPU utilization for parallel processing
- Memory footprint monitoring
- Garbage collection impact
Monitoring and Metrics
- Performance tracking per promise
- Aggregate timing statistics
- Memory usage patterns
- Error rate monitoring
Testing ​
typescript
// Test successful case
const successTest = promiseAll([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3),
]).then((results) => {
console.assert(
JSON.stringify(results) === '[1,2,3]',
'Should resolve with all values'
);
});
// Test rejection case
const failTest = promiseAll([
Promise.resolve(1),
Promise.reject('error'),
Promise.resolve(3),
]).catch((error) => {
console.assert(error === 'error', 'Should reject with first error');
});
// Test empty array
const emptyTest = promiseAll([]).then((results) => {
console.assert(
Array.isArray(results) && results.length === 0,
'Should resolve with empty array'
);
});