Async Task Scheduler in Vue.js
This challenge requires you to build a reusable Vue.js component that can manage and execute a queue of asynchronous tasks. This is crucial for scenarios where you need to perform multiple non-blocking operations sequentially or with specific delay intervals, providing better user experience by avoiding UI freezes and managing resource usage effectively.
Problem Description
You need to create a Vue.js component, implemented in TypeScript, that acts as an asynchronous task scheduler. This scheduler should allow users to add asynchronous functions (tasks) to a queue and execute them one after another. The component should support basic scheduling features like immediate execution, delayed execution, and the ability to track the progress of the tasks.
Key Requirements:
- Task Queueing: The component must maintain a queue of tasks waiting to be executed.
- Asynchronous Task Execution: Each task will be an asynchronous function (returning a Promise).
- Immediate Execution: Tasks should be executable immediately upon being added if the scheduler is idle.
- Delayed Execution: Support for scheduling a task to run after a specified delay.
- Progress Tracking: The component should expose reactive properties to indicate:
isIdle: Boolean, true if no tasks are currently running or queued for execution.isRunning: Boolean, true if at least one task is currently executing.completedTasks: Number, the count of tasks that have successfully completed.totalTasks: Number, the total number of tasks that have been added to the queue.
- Task Management: Provide methods to:
addTask(task: () => Promise<any>, delay?: number): Adds a new asynchronous task to the queue.delayis optional and specifies the delay in milliseconds before execution.run(): Manually triggers the execution of the next task in the queue if the scheduler is idle and there are tasks.clearQueue(): Removes all pending tasks from the queue.
- Error Handling: If a task rejects its promise, the scheduler should log the error and continue processing the remaining tasks. The
isRunningstate should be updated accordingly. - Reactivity: All progress tracking properties (
isIdle,isRunning,completedTasks,totalTasks) must be reactive using Vue's reactivity system.
Expected Behavior:
- When
addTaskis called, the task is added to the queue. - If the scheduler is not
isRunningand no delays are active, the task (or the next scheduled task) starts executing. - If a
delayis provided, the task waits for that duration before execution. - Tasks are executed sequentially. A new task only starts after the previous one has resolved or rejected.
- The progress tracking properties should update dynamically as tasks are added and executed.
isIdleshould betrueonly whentotalTasksis 0 or when all added tasks have been completed or cleared.isRunningshould betruewhen a task is actively being processed (after any delay has passed and before it resolves/rejects).
Edge Cases to Consider:
- Adding tasks when the scheduler is already running.
- Adding tasks with a delay of 0.
- What happens if a task throws a synchronous error before returning a promise? (Assume tasks are well-formed async functions returning Promises for this challenge).
- Clearing the queue while tasks are running or pending.
Examples
Example 1: Basic Task Execution
// Assuming a Vue component context where scheduler is initialized
// Create a sample async task
const asyncTask1 = () => new Promise(resolve => setTimeout(() => {
console.log('Task 1 completed');
resolve('Result 1');
}, 1000));
const asyncTask2 = () => new Promise(resolve => setTimeout(() => {
console.log('Task 2 completed');
resolve('Result 2');
}, 500));
// Add tasks to the scheduler
scheduler.addTask(asyncTask1);
scheduler.addTask(asyncTask2);
// Expected Output in the console:
// Task 1 completed
// Task 2 completed
// Expected state updates (simplified representation):
// Initially: isIdle: true, isRunning: false, completedTasks: 0, totalTasks: 0
// After addTask(asyncTask1): isIdle: true, isRunning: false, completedTasks: 0, totalTasks: 1
// When Task 1 starts: isIdle: false, isRunning: true, completedTasks: 0, totalTasks: 1
// After Task 1 completes: isIdle: false, isRunning: true, completedTasks: 1, totalTasks: 1
// When Task 2 starts: isIdle: false, isRunning: true, completedTasks: 1, totalTasks: 2
// After Task 2 completes: isIdle: true, isRunning: false, completedTasks: 2, totalTasks: 2
Example 2: Delayed Task Execution
// Assuming a Vue component context where scheduler is initialized
const delayedTask = () => new Promise(resolve => {
console.log('Delayed task executed');
resolve('Delayed Result');
});
// Add a task with a 2-second delay
scheduler.addTask(delayedTask, 2000);
// Expected Output in the console (after 2 seconds):
// Delayed task executed
// Expected state updates:
// Initially: isIdle: true, isRunning: false, completedTasks: 0, totalTasks: 0
// After addTask(delayedTask, 2000): isIdle: true, isRunning: false, completedTasks: 0, totalTasks: 1
// After 2 seconds, when delayedTask starts: isIdle: false, isRunning: true, completedTasks: 0, totalTasks: 1
// After delayedTask completes: isIdle: true, isRunning: false, completedTasks: 1, totalTasks: 1
Example 3: Handling Task Rejection and Clearing Queue
// Assuming a Vue component context where scheduler is initialized
const successfulTask = () => new Promise(resolve => setTimeout(() => resolve('Success'), 500));
const failingTask = () => new Promise((_, reject) => setTimeout(() => reject(new Error('Something went wrong')), 750));
const anotherSuccessfulTask = () => new Promise(resolve => setTimeout(() => resolve('Another Success'), 300));
// Add tasks
scheduler.addTask(successfulTask);
scheduler.addTask(failingTask);
scheduler.addTask(anotherSuccessfulTask);
// In a separate event handler or after some time:
// scheduler.clearQueue();
// Expected Console Output (if clearQueue is NOT called):
// (after 0.5s) successfulTask resolved
// (after 0.75s) Error: Something went wrong
// (after 0.75s + 0.3s) anotherSuccessfulTask resolved
// Expected Console Output (if clearQueue is called immediately after adding tasks):
// (after 0.5s) successfulTask resolved
// (and then the queue is cleared, so failingTask and anotherSuccessfulTask are not executed)
// Expected state updates (if clearQueue is NOT called):
// ... After failingTask completes: isIdle: false, isRunning: true, completedTasks: 1, totalTasks: 3
// ... After anotherSuccessfulTask completes: isIdle: true, isRunning: false, completedTasks: 2, totalTasks: 3 (Note: completedTasks doesn't count rejections)
// Expected state updates (if clearQueue IS called immediately):
// After clearQueue(): isIdle: true, isRunning: false, completedTasks: 0, totalTasks: 0 (all pending tasks are removed)
Constraints
- The scheduler component must be implemented using Vue 3 Composition API and TypeScript.
- All reactive state must be managed using Vue's
reforreactive. - Task execution should not block the main thread.
- The
delayparameter foraddTaskwill be a non-negative integer. - The maximum number of concurrently executing tasks is implicitly one (sequential execution).
- The maximum number of tasks in the queue is not strictly limited but should be handled efficiently.
Notes
- Consider using
setTimeoutfor implementing the delays. - You will need to manage the state of the scheduler carefully, especially when tasks complete or reject.
- Think about how to chain the execution of promises.
async/awaitcan be very helpful here. - The
completedTaskscount should only increment for successfully resolved promises. - The challenge is about creating a utility component, so focus on its logic and API rather than complex UI rendering. You can assume it will be used within a larger Vue application.