Hone logo
Hone
Problems

JavaScript Task Scheduler

You need to build a robust task scheduler in JavaScript. This scheduler will allow users to add tasks that need to be executed at specific times or after a certain delay. This is a fundamental component for many applications, from simple reminders to complex background processing.

Problem Description

The goal is to create a TaskScheduler class that can manage and execute tasks. The scheduler should support adding tasks with different scheduling mechanisms:

  • Immediate execution: Tasks that should run as soon as possible.
  • Delayed execution: Tasks that should run after a specified delay in milliseconds.
  • Scheduled execution: Tasks that should run at a specific future Date.
  • Recurring tasks: Tasks that should run repeatedly at a fixed interval.

The scheduler should also provide functionality to cancel scheduled tasks before they are executed.

Key Requirements:

  1. addTask(taskFn, options) Method:

    • taskFn: A function representing the task to be executed.
    • options (optional): An object to configure the task's scheduling.
      • delay: Number of milliseconds to wait before executing the task (for delayed execution).
      • scheduleAt: A Date object specifying the exact time to execute the task.
      • interval: Number of milliseconds for recurring tasks. If interval is provided, the task will run repeatedly every interval milliseconds. The task will be scheduled to run for the first time after delay or scheduleAt if those are also provided. If only interval is provided, it will run immediately and then repeat.
      • runOnce: If true and interval is also provided, the task will only run once after the initial delay/schedule. This is to prevent unintended infinite loops if interval is set but the user only wants a single delayed execution after a potential first interval.
  2. cancelTask(taskId) Method:

    • Accepts a unique taskId returned by addTask.
    • Removes the task from the scheduler, preventing it from executing.
  3. Task Identification: The addTask method should return a unique identifier for each added task, which will be used for cancellation.

  4. Error Handling: Tasks should be wrapped in try...catch blocks to prevent a single failing task from crashing the entire scheduler. Log errors to the console.

Expected Behavior:

  • Tasks scheduled with delay should execute after the specified milliseconds.
  • Tasks scheduled with scheduleAt should execute precisely at the specified Date (within reasonable system timing limits).
  • Recurring tasks with interval should execute repeatedly at the given interval.
  • Cancellation should stop a task from running, whether it's pending, scheduled, or currently running (though stopping a task mid-execution is not a requirement, cancellation only affects future executions).

Edge Cases:

  • Adding tasks with no options (immediate execution).
  • Canceling a task that has already executed.
  • Canceling a task that was never added.
  • Tasks that throw errors during execution.
  • Tasks with interval and runOnce: true.

Examples

Example 1: Delayed Execution

const scheduler = new TaskScheduler();

const taskId = scheduler.addTask(() => {
    console.log("Task 1 executed after 2 seconds.");
}, { delay: 2000 });

// Expected Output (after 2 seconds):
// Task 1 executed after 2 seconds.

Example 2: Scheduled Execution and Cancellation

const scheduler = new TaskScheduler();
const now = new Date();
const futureTime = new Date(now.getTime() + 5000); // 5 seconds from now

const taskId = scheduler.addTask(() => {
    console.log("Task 2 executed at the scheduled time.");
}, { scheduleAt: futureTime });

console.log(`Task 2 scheduled for: ${futureTime.toLocaleTimeString()}`);

// Cancel the task before it executes
setTimeout(() => {
    scheduler.cancelTask(taskId);
    console.log(`Task 2 with ID ${taskId} has been canceled.`);
}, 3000); // Cancel after 3 seconds

// Expected Output:
// Task 2 scheduled for: [time 5 seconds from now]
// Task 2 with ID [some ID] has been canceled.
// (Task 2 executed at the scheduled time. will NOT be printed)

Example 3: Recurring Task

const scheduler = new TaskScheduler();

const taskId = scheduler.addTask(() => {
    console.log(`Recurring task executed at: ${new Date().toLocaleTimeString()}`);
}, { interval: 3000 }); // Execute every 3 seconds

// Stop the recurring task after 10 seconds
setTimeout(() => {
    scheduler.cancelTask(taskId);
    console.log(`Recurring task with ID ${taskId} has been stopped.`);
}, 10000);

// Expected Output (will appear multiple times):
// Recurring task executed at: [time]
// Recurring task executed at: [time + 3s]
// Recurring task executed at: [time + 6s]
// Recurring task executed at: [time + 9s]
// Recurring task with ID [some ID] has been stopped.

Example 4: Task with Delay and Interval, then runOnce

const scheduler = new TaskScheduler();

const taskId = scheduler.addTask(() => {
    console.log(`This task should run once after a delay of 1 second.`);
}, { delay: 1000, interval: 5000, runOnce: true });

// Expected Output (after 1 second):
// This task should run once after a delay of 1 second.
// (The task will NOT repeat because runOnce is true)

Constraints

  • The TaskScheduler class should be implemented using standard JavaScript features.
  • No external libraries are allowed for the core scheduling logic.
  • The taskId returned by addTask must be unique for each task instance.
  • The scheduler should be reasonably efficient; avoid busy-waiting.

Notes

  • Consider using setTimeout and setInterval for implementing delayed and recurring tasks.
  • For scheduled tasks (scheduleAt), you'll need to calculate the delay required to reach the scheduleAt time.
  • Think about how to manage the state of tasks (pending, running, canceled).
  • Ensure your task IDs are truly unique. A simple counter or a UUID generator could be considered.
  • The Date object in JavaScript can be tricky. Pay attention to time zones and how getTime() works.
Loading editor...
javascript