Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Task Scheduler Service on JavaScript for WEB

5.00/5 (4 votes)
14 Aug 2015CPOL5 min read 24.5K   471  
Task Scheduler JavaScript library

Introduction

Task Scheduler is the useful and important functionality of operating systems. For Windows, it is Task Scheduler for Linux, it is Crontab, on MAC is Cron. At first sight, the stuff is doing a simple job, but it is important to enable you to automatically perform routine tasks on a computer. It does this by monitoring whatever criteria you choose to initiate the tasks (referred to as triggers) and then executing the tasks when the criteria is met. The most common tasks which can be scheduled are starting an application, sending an email message, showing a message, or backing up sensitive user data.

Tasks can be scheduled to run in different ways, for example:

  • When a specific system event occurs
  • At a specific time
  • At a specific time on a daily schedule
  • At a specific time on a weekly schedule
  • At a specific time on a monthly schedule
  • At a specific time on a monthly day-of-week schedule
  • When the computer enters an idle state

I would introduce the library which provides the task scheduling for web application and based on JavaScript. Now, the most of web applications have thick client and the most of the stuff is running on client and making request to server just to update data. Such clients are making a lot to handle user input and managing of background tasks, so it becomes closer by functionality to operating systems. Where task scheduler has an important role.

At the beginning of the project, it was a simple part of replication system. And the main goal was to create and edit existence Crontab scheduling pattern. But as usual, it happens the project was growing by adding to them new feature and now it is the whole task scheduler.

Contents

Learnings

The data structure is composed of two parts, scheduler and task management. The scheduler system is making all stuff to parse, create, edit scheduling Crontab format based string, and also it is calculating next and previous scheduled date according to the pattern, as well, preparing human readable string of occurrences.

As an extra feature, it calculates all next and previous scheduled dates till any specified by user.

The scheduler data structure consists of scheduler class and six helper classes: minuteContainer, hourContainer, dayContainer, monthContainer, weekContainer and baseContainer which encapsulates common functionality and is super class for them.

The scheduler operates with Crontab based string on his input of the following format:

JavaScript
// 

.------------------- minute (0 - 59)                     
|  .---------------- hour (0 - 23) 
|  |  .------------- day of month (1 - 31) 
|  |  |  .---------- month (1 - 12) 
|  |  |  |  .------- day of week (0 - 7) (Sunday=0,7) 
|  |  |  |  | 

*  *  *  *  *
//

According to the pattern, the minutes, hours, days, months and days of week containers will be created with memorized values. The container is an array of values formed on the basis of input data and a set of methods to manipulate them. The array is formed in such a manner so the value of array and his distribution should correspond to input pattern and value of the array should be easily retrieved by index. For example, if we have pattern “*/5 2/3 * * *” so we will have minutes container according to first parameter */5 equals to [0, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 15, 15, 15, 15, 15, …, 55, 55, 55, 55, 55]. It allows to avoid loops during calculating next occurrence. In order to get next occurrence, we need simply to retrieve value from array by index for current minute of the date, for example for date: 08/12/2015 (15:03) we have current minute 03 and in the container by the index 03 we will have 5 value, so the next occurrence is going to be 08/12/2015 (17:05), because it happens every 3 hours starting at 2 and every 5 minutes starting at 0.

The question may arise, why it operates with arrays of hashed values of minutes, hours, etc. and not with just milliseconds. Both ways are possible. Probably, the current solution first comes to mind and appeared simpler.

The current implementation of scheduler takes almost all formats of Crontab string, so it can be "*/5 2/3 * * *" , "20/5 2/3 1-2,4-7 * *", "1,2,3,4,5 2/3 1-2,4-7 * *". Also it has methods to form containers by adding or removing elements from array, for example like: addMinute(m), removeMinute(m), ...

Additionally, the scheduler prints out human readable string something like that: “Occurs every 3 hours starting at 02:00, every day, every month.” Unfortunately, it is not localized for now.

One more feature which can be interesting is calculating all occurrences till the specific date. And to avoid freezing of browser during calculation dates for very detailed pattern (e.g. “*/5 * * * *” ) during one year, the operation is divided into small chunks and a timer is used to delay the processing of each chunk to allow browser for the time to switch to different task. The default size of chunk is 100 elements or you can specify own by passing any number at third parameter of the function getAllNextTill(...).

JavaScript
/* set maxSyncLoadEl to -1 to have traditional loop */
this.getAllNextTill = function (endDate, callback, maxSyncLoadEl)
{
    var _allDates = Array();
    var _maxSyncLoadEl = typeof maxSyncLoadEl != 'undefined' && 
    	scheduler.isNumber(maxSyncLoadEl) ? maxSyncLoadEl : maxSyncLoadElDef;
    var _callback = callback;
    var _startDate = startDate;
    var _syncLoadedEl = 0;

    if (_startDate != null && _startDate instanceof Date)
    {
        var scope = this;
        (function loop()
        {
            try
            {
                while(_maxSyncLoadEl == -1 || _syncLoadedEl <= _maxSyncLoadEl)
                {
                    if (scope.previous())
                    {
                        var curDate = scope.getDateStamp();
                        if (_startDate < curDate)
                        {
                            _allDates.push(curDate);
                        }
                        else
                        {
                            if (callback)
                            {
                                callback(_allDates);
                            }
                            return;
                        }
                    }

                    _syncLoadedEl++;
                }

                _syncLoadedEl = 0;
                setTimeout(loop, 1);
            }
            catch (e)
            {
                throw e;
            }
        })();
    }
    else
    {
        throw new InvalidArgumentException();
    }
}
//

The taskManger and task objects are making all jobs by the scheduling and triggering tasks. Task manager encapsulates methods to manage tasks. The task object encapsulates methods to run job at specific time which is set by scheduler or it can be forcibly launched now.

Example of scheduling of next occurrence of task:

JavaScript
this.start = function ()
{
    _isStarted = true;
    continuouslyRun(true);
};

function continuouslyRun(skip)
{
    if (_isStarted)
    {
        if (!skip)
        {
            runOnce();
        }

        //schedule next time of running
        var currDate = new Date();
        _scheduler.setCurrentTimeExt(currDate);
        if (_scheduler.next())
        {
            var nextDate = _scheduler.getDateStamp();
            var timeout = nextDate.getTime() - currDate.getTime();
            _timerId = setTimeout(continuouslyRun, timeout);
        }
    }
};
function runOnce()
{
    _isRunning = true;

    if (_action)
    {
        if (!_args)
        {
            _action(scope);
        }
        else
        {
            _action(scope, _args);
        }
    }

    _isRunning = false;
};
//

Data Model

Image 1

Sample of Use

For now, it is just set of methods to add/remove tasks, enumerate existence and start/stop manager. But I have plans to extend the taskManager interface, add more methods to manage tasks, enable/disable methods and methods to accumulate statistics. Also, it will be more properly to load dictionary and formats of human readable string of scheduler on-the-fly according to language selected at browser or specified by user.

JavaScript
// 
            var tasks = new scheduler.taskManager();

            tasks.addTask(new scheduler.task("0/2  *   *   *   *", function (task, args)
            {
                //to do something
            }, "done"));

            tasks.addTask(new scheduler.task("0/10  *   *   *   *", function (task, args)
            {
                //to do something
            }, "done"));

            var _task = new scheduler.task("0/5  *   *   *   *", function (task, args)
            {
                //to do something
            }, "done");
            
            tasks.addTask(_task);

            tasks.enumerate(function (task, args)
            {
                //to do something
            });

            tasks.start();
            
            // to remove task from scheduling
            tasks.removeTask(_task);
//

How to Use

The source taskScheduler_src.zip contains not obfuscated or minimized source code. To see how it works, just to run index.html file. At source, you will find files of task management system and scheduler itself, and utils file, also there are couple files to demonstrate how it works. It can be wrapped and used as jQuery addon or used directly, it doesn't have any dependencies and can be used as is.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)