occaecatidicta
Version:
152 lines (122 loc) • 3.32 kB
text/typescript
/**
* The main class and interface of the schedule module
*/
import * as PriorityQueue from './priorityQueue';
import * as Job from './job';
import { SimpleTriggerOpts } from './simpleTrigger';
import { getLogger } from 'omelox-logger';
import * as path from 'path';
let logger = getLogger('omelox-scheduler', path.basename(__filename));
let timerCount = 0;
let map: {[key: number]: Job.Job} = {};
let queue = PriorityQueue.createPriorityQueue(comparator);
let jobId = 0;
let timer: any;
// The accuracy of the scheduler, it will affect the performance when the schedule tasks are
// crowded together
let accuracy = 10;
/**
* Schedule a new Job
* @param trigger The trigger to use
* @param jobFunc The function the job to run
* @param jobData The data the job use
* @return The job id, which can be canceled by cancelJob(id:number)
*/
function scheduleJob<T>(trigger: SimpleTriggerOpts | string, jobFunc: (data?: T) => void, jobData?: T): number {
let job: Job.Job = Job.createJob(trigger, jobFunc, jobData);
let excuteTime = job.excuteTime();
let id = job.id;
map[id] = job;
let element = {
id: id,
time: excuteTime
};
let curJob = queue.peek();
if (!curJob || excuteTime < curJob.time) {
queue.offer(element);
setTimer(job);
return job.id;
}
queue.offer(element);
return job.id;
}
/**
* Cancel Job
*/
function cancelJob(id: number): boolean {
let curJob = queue.peek();
if (curJob && id === curJob.id) { // to avoid queue.peek() is null
queue.pop();
delete map[id];
clearTimeout(timer);
excuteJob();
}
delete map[id];
return true;
}
/**
* Clear last timeout and schedule the next job, it will automaticly run the job that
* need to run now
* @param job The job need to schedule
* @return void
*/
function setTimer(job: Job.Job) {
clearTimeout(timer);
timer = setTimeout(excuteJob, job.excuteTime() - Date.now());
}
/**
* The function used to ran the schedule job, and setTimeout for next running job
*/
function excuteJob() {
let job = peekNextJob();
let nextJob;
while (!!job && (job.excuteTime() - Date.now()) < accuracy) {
job.run();
queue.pop();
let nextTime = job.nextTime();
if (nextTime === null) {
delete map[job.id];
} else {
queue.offer({ id: job.id, time: nextTime });
}
job = peekNextJob();
}
// If all the job have been canceled
if (!job)
return;
// Run next schedule
setTimer(job);
}
/**
* Return, but not remove the next valid job
* @return Next valid job
*/
function peekNextJob() {
if (queue.size() <= 0)
return null;
let job = null;
do {
job = map[queue.peek().id];
if (!job) queue.pop();
} while (!job && queue.size() > 0);
return (!!job) ? job : null;
}
/**
* Return and remove the next valid job
* @return Next valid job
*/
function getNextJob() {
let job = null;
while (!job && queue.size() > 0) {
let id = queue.pop().id;
job = map[id];
}
return (!!job) ? job : null;
}
function comparator(e1: {time: number}, e2: {time: number}) {
return e1.time > e2.time;
}
export
{
scheduleJob, cancelJob
};