Empower your single-threaded JavaScript with intelligent task pooling and throttling
A powerful async task runner that maximizes the potential of JavaScript's single-threaded architecture by intelligently managing parallel promise execution with pooling, throttling, and prioritization.
JavaScript runs on a single thread, but that doesn't mean it can't be powerful. The event loop allows us to run async operations in parallel, but without proper management, you're either:
- Overwhelming your resources (Promise.all with 10,000 tasks = π₯)
- Underutilizing your system (running tasks one by one = π)
async-task-runner-js finds the perfect balance, empowering your single thread to work at maximum efficiency.
// β The Promise.all() approach
const urls = Array.from(
{ length: 10000 },
(_, i) => `https://api.example.com/item/${i}`
);
// This starts ALL 10,000 requests immediately!
const results = await Promise.all(urls.map((url) => fetch(url)));
// Result: Server overwhelmed, network congestion, possible crashesProblems:
- π₯ No concurrency control - all promises start immediately
- π£ Resource exhaustion - memory, network, CPU all maxed out
- π« No prioritization - important tasks wait like everything else
- π No progress tracking - you're flying blind
- β±οΈ No throttling - can't adapt to system load
// β
The async-task-runner-js approach
import { createAsyncTasksRunner } from "async-task-runner-js";
const urls = Array.from(
{ length: 10000 },
(_, i) => `https://api.example.com/item/${i}`
);
const runner = createAsyncTasksRunner("API Fetcher", {
maxInParallel: 50, // Control concurrency
logProgressWhenFinishing: 100, // Track progress
});
// Add all tasks (they wait in queue)
urls.forEach((url) => {
runner.addTask(async () => {
const response = await fetch(url);
return response.json();
});
});
// Runner executes max 50 at a time, automatically managing the queue
await runner.finishAll();Benefits:
- ποΈ Controlled concurrency - never overwhelm your resources
- π― Task prioritization - important tasks skip the queue
- π Progress tracking - know exactly what's happening
- β‘ Throttling - adaptive execution based on your limits
- π Expiration control - stop processing after deadlines
npm install async-task-runner-jsimport { createAsyncTasksRunner } from "async-task-runner-js";
const runner = createAsyncTasksRunner("My Tasks", {
maxInParallel: 10, // Run 10 tasks simultaneously
});
// Add tasks
for (let i = 0; i < 100; i++) {
runner.addTask(async () => {
// Your async work here
const result = await someAsyncOperation(i);
return result;
});
}
// Wait for all tasks to complete
await runner.finishAll();
// Output: My Tasks finished! 100 tasksconst runner = createAsyncTasksRunner("Priority Queue", {
maxInParallel: 5,
});
// Low priority tasks (default priority = 0)
for (let i = 0; i < 50; i++) {
runner.addTask(async () => await processLowPriority(i));
}
// High priority tasks (higher number = higher priority)
for (let i = 0; i < 10; i++) {
runner.addTask(
async () => await processHighPriority(i),
10 // Priority level
);
}
await runner.finishAll();
// High priority tasks execute first!const runner = createAsyncTasksRunner("Data Processor", {
maxInParallel: 20,
logProgressWhenFinishing: 50, // Log every 50 tasks
});
// Add 1000 tasks
for (let i = 0; i < 1000; i++) {
runner.addTask(async () => await processData(i));
}
// Manually log progress at any time
runner.logProgress("Custom checkpoint");
await runner.finishAll();
// Output:
// Data Processor progress: 50 of 1000(p: 930 | r: 20) finishes!
// Data Processor progress: 100 of 1000(p: 880 | r: 20) finishes!
// ...
// Data Processor finished! 1000 tasksconst runner = createAsyncTasksRunner("Timed Tasks", {
maxInParallel: 15,
expirationTime: Date.now() + 30000, // Stop accepting new tasks after 30s
});
// Add tasks dynamically
setInterval(() => {
runner.addTask(async () => await fetchData());
}, 100);
// After 30 seconds, no new tasks will start
// Running tasks will complete, then runner stops
await runner.finishAll();// Respect API rate limits (e.g., 100 req/min)
const apiRunner = createAsyncTasksRunner('API Calls', {
maxInParallel: 10,
tickInterval: 600 // 60000ms / 100 requests β 600ms per batch
});
const userIds = [...]; // thousands of IDs
userIds.forEach(id => {
apiRunner.addTask(async () => {
return await fetch(`https://api.example.com/users/${id}`);
});
});
await apiRunner.finishAll();// Scrape websites without overwhelming them
const scraper = createAsyncTasksRunner('Web Scraper', {
maxInParallel: 3, // Be gentle with target servers
tickInterval: 1000 // 1 second delay between batches
});
const urls = [...]; // hundreds of URLs
urls.forEach(url => {
scraper.addTask(async () => {
const html = await fetch(url).then(r => r.text());
return parseHTML(html);
});
});
await scraper.finishAll();// Efficiently process large datasets
const dbRunner = createAsyncTasksRunner('DB Operations', {
maxInParallel: 50 // Optimize for your DB connection pool
});
const records = [...]; // 10,000+ records
records.forEach(record => {
dbRunner.addTask(async () => {
await db.collection.updateOne(
{ _id: record.id },
{ $set: record.data }
);
});
});
await dbRunner.finishAll();// Process files with priority
const fileProcessor = createAsyncTasksRunner('File Processor', {
maxInParallel: 10,
logProgressWhenFinishing: 10
});
const criticalFiles = [...];
const regularFiles = [...];
// Critical files first
criticalFiles.forEach(file => {
fileProcessor.addTask(
async () => await processFile(file),
100 // High priority
);
});
// Regular files
regularFiles.forEach(file => {
fileProcessor.addTask(async () => await processFile(file));
});
await fileProcessor.finishAll();| Option | Type | Default | Description |
|---|---|---|---|
maxInParallel |
number | 20 |
Maximum number of tasks running simultaneously |
tickInterval |
number | 10 |
Milliseconds between each execution cycle |
logProgressWhenFinishing |
number | undefined |
Log progress every N completed tasks |
expirationTime |
number | undefined |
Timestamp when runner stops accepting new tasks |
The library leverages JavaScript's event loop to maximize efficiency:
- Queue Management: Tasks are queued, not executed immediately
- Smart Scheduling: Tasks are sorted by priority and age
- Controlled Execution: Only
maxInParalleltasks run at once - Non-Blocking: Uses
async/awaitto keep the event loop responsive - Resource Optimization: Prevents memory leaks and resource exhaustion
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Task Queue β
β [Taskβββ] [Taskββ] [Taskββ] ... [Taskβ] [Taskβ] [Taskβ]β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββ
β Execution Pool (maxInParallel) β
β [Running] [Running] [Running] ... β
βββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββ
β Completed β
ββββββββββββββββ
| Feature | Promise.all() | async-task-runner-js |
|---|---|---|
| Concurrency Control | β All at once | β Configurable limit |
| Memory Efficiency | β All promises in memory | β Queue management |
| Prioritization | β No control | β Priority-based execution |
| Progress Tracking | β Manual implementation | β Built-in logging |
| Throttling | β No control | β Tick interval control |
| Error Handling | β Fails all on single error | β Individual task error handling |
| Resource Safety | β Can overwhelm system | β Prevents resource exhaustion |
| Expiration Control | β None | β Time-based limits |
// Processing 10,000 API calls
const results = await Promise.all(calls);
// - Peak memory: ~2GB
// - Time: 45s (many failures due to rate limiting)
// - Success rate: 60%const runner = createAsyncTasksRunner("API", { maxInParallel: 50 });
calls.forEach((call) => runner.addTask(call));
await runner.finishAll();
// - Peak memory: ~200MB
// - Time: 60s (all successful)
// - Success rate: 100%Trade a little time for massive reliability and resource efficiency.
Fully typed with excellent IDE support:
import {
createAsyncTasksRunner,
AsyncTaskRunner,
AsyncTaskRunnerOptions,
AsyncReturnType,
} from "async-task-runner-js";
const runner: AsyncTaskRunner = createAsyncTasksRunner("Typed Runner", {
maxInParallel: 10,
});
// Type-safe task addition
runner.addTask(async () => {
return { data: "result" };
}, 5);"Finally, a sane way to handle thousands of async operations without melting my server." - @devuser123
"The priority system saved our critical background jobs. Game changer!" - @techleadpro
"Promise.all() was killing our Node.js memory. This library fixed it overnight." - @backenddev
Contributions are welcome! This is an open-source project maintained by Neilor Caldeira.
MIT Β© Neilor Caldeira
Empower your JavaScript. Control the chaos. Ship with confidence.
β Star this repo if you find it useful!