mirror of
https://github.com/bendtherules/JSQuestions.git
synced 2026-08-18 13:53:51 +00:00
async scheduler 1
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
# Async Task Scheduler (Concurrency Limiter)
|
||||
|
||||
## Summary
|
||||
Implement a `TaskScheduler` class that allows adding asynchronous tasks. The scheduler should execute tasks but ensure that no more than N tasks run continuously at the same time. As soon as one finishes, the next pending task should start.
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
* `new TaskScheduler(concurrency)` - The class `TaskScheduler` should accept concurrency (integer) in the constructor. This is the maximum number of tasks which be executed simultaneously.
|
||||
* `TaskScheduler.addTask(taskFn) : Promise` - Adds `taskFn` to task queue. Returns a Promise that resolves/rejects when the actual task finishes. `taskFn` is a function that returns a Promise.
|
||||
* `TaskScheduler.play()` - Puts the execution in 'play' mode.
|
||||
* `TaskScheduler.pause()` - Puts the execution in 'paused' mode.
|
||||
* In 'play' mode, new tasks from the queue will get picked up and processed.
|
||||
* In 'paused' mode, no new tasks will get processed. Already running tasks will run till finish.
|
||||
* By default, new `TaskScheduler` instances should be in 'paused' mode.
|
||||
* Tasks should be executed in First-In-First-Out (FIFO) order.
|
||||
* (In play mode) If the number of running tasks is less than concurrency, start new task immediately.
|
||||
* (In play mode) If the concurrency limit is reached, queue the task.
|
||||
* When a running task completes (resolves or rejects), automatically pick the next task from the queue.
|
||||
*
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
```JavaScript
|
||||
class TaskScheduler {
|
||||
constructor(concurrency) {
|
||||
this.concurrency = concurrency;
|
||||
this.running = 0;
|
||||
this.queue = [];
|
||||
}
|
||||
|
||||
addTask(taskFn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const taskWrapper = async () => {
|
||||
this.running++;
|
||||
try {
|
||||
const result = await taskFn();
|
||||
resolve(result);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
} finally {
|
||||
this.running--;
|
||||
this.runNext();
|
||||
}
|
||||
};
|
||||
|
||||
if (this.running < this.concurrency) {
|
||||
taskWrapper();
|
||||
} else {
|
||||
this.queue.push(taskWrapper);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
runNext() {
|
||||
if (this.queue.length > 0 && this.running < this.concurrency) {
|
||||
const nextTask = this.queue.shift();
|
||||
nextTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
const scheduler = new TaskScheduler(2);
|
||||
const delay = (ms, val) => new Promise(r => setTimeout(() => r(val), ms));
|
||||
|
||||
scheduler.addTask(() => delay(1000, 'A')).then(console.log);
|
||||
scheduler.addTask(() => delay(500, 'B')).then(console.log);
|
||||
scheduler.addTask(() => delay(300, 'C')).then(console.log);
|
||||
// Output: B (at 500ms), A (at 1000ms), C (at 1300ms) - C waits for B to finish.
|
||||
```
|
||||
|
||||
|
||||
## Follow-ups
|
||||
|
||||
* Priority Queue: Modify addTask to accept a priority level (High, Low). Each priority level gets its own queue. Tasks from the High-priority queue should be picked up first. Only when it is empty, Low-priority tasks will get picked up.
|
||||
* Cancellation: Return a cancel function from addTask (alongwith the promise). If cancelled while in the queue, it should never run.
|
||||
* Error Handling: What happens if taskFn throws synchronously instead of returning a Promise? Ensure the scheduler doesn't crash.
|
||||
Reference in New Issue
Block a user