smartMap()
Async-aware map function with concurrency control, filtering, and error handling.
Utility Function
Async
Arrays
Syntax
smartMap(array, mapper, options)Parameters
array Array
The array to map over.
mapper Function
Function to apply to each element. Can be async.
options object optional
Configuration options.
concurrency- Maximum concurrent operations (default: 5)filter- Filter function to apply before mappingerrorHandler- Custom error handling functionpreserveOrder- Maintain original array order (default: true)
Returns
Promise<Array>
- Promise resolving to the mapped array.Examples
Basic Async Mapping
// Simple async transformation
const numbers = [1, 2, 3, 4, 5];
const doubled = await smartMap(numbers, async (num) => {
await new Promise(resolve => setTimeout(resolve, 100));
return num * 2;
});
console.log(doubled); // [2, 4, 6, 8, 10]Concurrency Control
// Limit concurrent operations
const urls = [
'https://api.example.com/user/1',
'https://api.example.com/user/2',
'https://api.example.com/user/3',
// ... more URLs
];
const users = await smartMap(
urls,
async (url) => {
const response = await fetch(url);
return response.json();
},
{ concurrency: 3 } // Only 3 requests at a time
);
console.log(users); // Array of user objectsWith Filtering
// Filter before mapping
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const processedEvens = await smartMap(
data,
async (num) => {
// Simulate async processing
await new Promise(resolve => setTimeout(resolve, 50));
return num * num;
},
{
filter: (num) => num % 2 === 0, // Only process even numbers
concurrency: 2
}
);
console.log(processedEvens); // [4, 16, 36, 64, 100]Error Handling
// Custom error handling
const mixedData = [1, 'invalid', 3, null, 5];
const results = await smartMap(
mixedData,
async (item) => {
if (typeof item !== 'number') {
throw new Error(`Invalid item: ${item}`);
}
return item * 2;
},
{
errorHandler: (error, item, index) => {
console.warn(`Error processing item ${index}:`, error.message);
return null; // Return null for failed items
}
}
);
console.log(results); // [2, null, 6, null, 10]File Processing
// Process files with concurrency control
const fs = require('fs').promises;
const path = require('path');
const filePaths = [
'file1.txt',
'file2.txt',
'file3.txt',
// ... more files
];
const fileContents = await smartMap(
filePaths,
async (filePath) => {
try {
const content = await fs.readFile(filePath, 'utf8');
return {
path: filePath,
content: content,
size: content.length,
lines: content.split('\n').length
};
} catch (error) {
throw new Error(`Failed to read ${filePath}: ${error.message}`);
}
},
{
concurrency: 5,
errorHandler: (error, filePath) => {
console.error(error.message);
return { path: filePath, error: error.message };
}
}
);
console.log(fileContents);Image Processing Pipeline
// Process images with filtering and transformation
const imageFiles = [
'photo1.jpg', 'document.pdf', 'photo2.png',
'video.mp4', 'photo3.gif', 'text.txt'
];
const processedImages = await smartMap(
imageFiles,
async (filename) => {
// Simulate image processing
console.log(`Processing ${filename}...`);
await new Promise(resolve => setTimeout(resolve, 1000));
return {
original: filename,
thumbnail: filename.replace(/\.[^.]+$/, '_thumb.jpg'),
processed: true,
timestamp: new Date().toISOString()
};
},
{
filter: (filename) => /\.(jpg|jpeg|png|gif)$/i.test(filename),
concurrency: 3,
preserveOrder: true
}
);
console.log(processedImages);
// Only image files are processed, PDFs and videos are filtered outDatabase Operations
// Batch database operations
const userIds = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const enrichedUsers = await smartMap(
userIds,
async (userId) => {
// Simulate database queries
const user = await db.users.findById(userId);
const profile = await db.profiles.findByUserId(userId);
const posts = await db.posts.findByUserId(userId);
return {
...user,
profile,
postCount: posts.length,
lastActivity: posts[0]?.createdAt || null
};
},
{
concurrency: 5, // Limit database connections
errorHandler: (error, userId) => {
console.error(`Failed to enrich user ${userId}:`, error);
return { id: userId, error: 'Failed to load' };
}
}
);
console.log(enrichedUsers);Performance Comparison
// Compare with regular Promise.all
const data = Array.from({ length: 100 }, (_, i) => i + 1);
// Regular Promise.all (all at once - can overwhelm resources)
console.time('Promise.all');
const results1 = await Promise.all(
data.map(async (num) => {
await new Promise(resolve => setTimeout(resolve, 100));
return num * 2;
})
);
console.timeEnd('Promise.all');
// smartMap with concurrency control
console.time('smartMap');
const results2 = await smartMap(
data,
async (num) => {
await new Promise(resolve => setTimeout(resolve, 100));
return num * 2;
},
{ concurrency: 10 }
);
console.timeEnd('smartMap');
// smartMap is more resource-friendly for large datasets