Here's a TypeScript scheduler using node-schedule library!
// First install the required packages: // npm install node-schedule @types/node-schedule import * as schedule from 'node-schedule'; import { Job } from 'node-schedule'; interface TaskConfig { name: string; schedule: string | Date; task: () => Promise<void> | void; enabled?: boolean; } class TaskScheduler { private jobs: Map<string, Job> = new Map(); /** * Schedule a task with cron-like syntax or specific date * @param config Task configuration * @returns Job instance */ scheduleTask(config: TaskConfig): Job | null { if (!config.enabled && config.enabled !== undefined) { console.log(`Task ${config.name} is disabled, skipping...`); return null; } try { const job = schedule.scheduleJob(config.name, config.schedule, async () => { console.log(`[${new Date().toISOString()}] Executing task: ${config.name}`); try { await config.task(); console.log(`[${new Date().toISOString()}] Task completed: ${config.name}`); } catch (error) { console.error(`[${new Date().toISOString()}] Task failed: ${config.name}`, error); } }); if (job) { this.jobs.set(config.name, job); console.log(`Task scheduled: ${config.name} - Next run: ${job.nextInvocation()}`); } return job; } catch (error) { console.error(`Failed to schedule task: ${config.name}`, error); return null; } } /** * Schedule multiple tasks at once * @param configs Array of task configurations */ scheduleTasks(configs: TaskConfig[]): void { configs.forEach(config => this.scheduleTask(config)); } /** * Cancel a specific task * @param taskName Name of the task to cancel */ cancelTask(taskName: string): boolean { const job = this.jobs.get(taskName); if (job) { job.cancel(); this.jobs.delete(taskName); console.log(`Task cancelled: ${taskName}`); return true; } console.warn(`Task not found: ${taskName}`); return false; } /** * Cancel all scheduled tasks */ cancelAllTasks(): void { this.jobs.forEach((job, name) => { job.cancel(); console.log(`Task cancelled: ${name}`); }); this.jobs.clear(); } /** * Get information about all scheduled tasks */ getTasksInfo(): Array<{name: string, nextRun: Date | null}> { const tasksInfo: Array<{name: string, nextRun: Date | null}> = []; this.jobs.forEach((job, name) => { tasksInfo.push({ name, nextRun: job.nextInvocation() }); }); return tasksInfo; } /** * Reschedule an existing task * @param taskName Name of the task to reschedule * @param newSchedule New schedule (cron or date) */ rescheduleTask(taskName: string, newSchedule: string | Date): boolean { const job = this.jobs.get(taskName); if (job) { const success = job.reschedule(newSchedule); if (success) { console.log(`Task rescheduled: ${taskName} - Next run: ${job.nextInvocation()}`); } return success; } console.warn(`Task not found: ${taskName}`); return false; } /** * Graceful shutdown - cancel all jobs */ shutdown(): void { console.log('Shutting down scheduler...'); this.cancelAllTasks(); schedule.gracefulShutdown(); } }
This implementation includes:
- Type Safety: Full TypeScript support with interfaces and proper typing
- Flexible Scheduling: Supports multiple scheduling formats:
- Cron-like syntax ('0 9 * * *' for daily at 9 AM)
- Specific dates (new Date())
- RecurrenceRule for complex patterns
- Task Management:
- Schedule single or multiple tasks
- Cancel specific tasks or all tasks
- Reschedule existing tasks
- Enable/disable tasks
- Get information about scheduled tasks
- Error Handling: Error handling with logging
- Graceful Shutdown: Proper cleanup on process termination
Cron Format Quick Reference:
* * * * * * ┬ ┬ ┬ ┬ ┬ ┬ │ │ │ │ │ │ │ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun) │ │ │ │ └───── month (1 - 12) │ │ │ └────────── day of month (1 - 31) │ │ └─────────────── hour (0 - 23) │ └──────────────────── minute (0 - 59) └───────────────────────── second (0 - 59, optional)
Common Patterns:
'*/5 * * * *' - Every 5 minutes
'0 */2 * * *' - Every 2 hours
'0 9-17 * * 1-5' - Every hour from 9 AM to 5 PM, Monday to Friday
'0 0 1 * *' - First day of every month at midnight
The scheduler handles async tasks, provides detailed logging, and includes graceful shutdown functionality. You can easily extend it with additional features like task persistence, retry logic, or notification systems.
Examples of how to use it
// Example usage const scheduler = new TaskScheduler(); // Example tasks const exampleTasks: TaskConfig[] = [ { name: 'daily-report', schedule: '0 9 * * *', // Every day at 9:00 AM task: async () => { console.log('Generating daily report...'); // Simulate async work await new Promise(resolve => setTimeout(resolve, 1000)); console.log('Daily report generated successfully'); }, enabled: true }, { name: 'database-cleanup', schedule: '0 2 * * 0', // Every Sunday at 2:00 AM task: async () => { console.log('Starting database cleanup...'); // Database cleanup logic here await new Promise(resolve => setTimeout(resolve, 2000)); console.log('Database cleanup completed'); }, enabled: true }, { name: 'health-check', schedule: '*/5 * * * *', // Every 5 minutes task: () => { console.log('Performing health check...'); // Health check logic here console.log('Health check passed'); }, enabled: true }, { name: 'monthly-summary', schedule: '0 0 1 * *', // First day of every month at midnight task: async () => { console.log('Generating monthly summary...'); await new Promise(resolve => setTimeout(resolve, 1500)); console.log('Monthly summary generated'); }, enabled: false // Disabled for this example }, { name: 'one-time-task', schedule: new Date(Date.now() + 10000), // Run once in 10 seconds task: () => { console.log('Executing one-time task...'); }, enabled: true } ]; // Schedule all tasks scheduler.scheduleTasks(exampleTasks); // Display scheduled tasks info console.log('\n=== Scheduled Tasks ==='); const tasksInfo = scheduler.getTasksInfo(); tasksInfo.forEach(task => { console.log(`${task.name}: Next run at ${task.nextRun}`); }); // Example of rescheduling a task after 30 seconds setTimeout(() => { console.log('\n=== Rescheduling health-check task ==='); scheduler.rescheduleTask('health-check', '*/10 * * * *'); // Change to every 10 minutes }, 30000); // Example of cancelling a specific task after 1 minute setTimeout(() => { console.log('\n=== Cancelling one-time-task ==='); scheduler.cancelTask('one-time-task'); }, 60000); // Graceful shutdown on process termination process.on('SIGINT', () => { console.log('\nReceived SIGINT, shutting down gracefully...'); scheduler.shutdown(); process.exit(0); }); process.on('SIGTERM', () => { console.log('\nReceived SIGTERM, shutting down gracefully...'); scheduler.shutdown(); process.exit(0); }); // Advanced scheduling examples: // Schedule with RecurrenceRule for more complex patterns const complexRule = new schedule.RecurrenceRule(); complexRule.dayOfWeek = [1, 3, 5]; // Monday, Wednesday, Friday complexRule.hour = 14; // 2 PM complexRule.minute = 30; // 30 minutes past the hour scheduler.scheduleTask({ name: 'weekday-notification', schedule: complexRule, task: () => { console.log('Weekday notification sent!'); }, enabled: true }); // Schedule for specific dates const specificDates = [ new Date(2024, 11, 25, 10, 0, 0), // Christmas Day 2024, 10:00 AM new Date(2025, 0, 1, 0, 0, 0), // New Year 2025, midnight ]; specificDates.forEach((date, index) => { scheduler.scheduleTask({ name: `holiday-task-${index + 1}`, schedule: date, task: () => { console.log(`Holiday task executed at ${date}`); }, enabled: true }); }); console.log('\nScheduler is running. Press Ctrl+C to exit.'); // Export the scheduler for use in other modules export { TaskScheduler, TaskConfig };
This is a small code snippet, hopefully someone find this implementation useful. As always feel free to express your thoughts and criticize this snippet! Cheers
Top comments (0)