101 lines
2.3 KiB
JavaScript
101 lines
2.3 KiB
JavaScript
'use strict'
|
|
|
|
const moment = require('moment-timezone')
|
|
|
|
const next = require('./next')
|
|
const parse = require('./parse')
|
|
|
|
const MAX_DELAY = 2 ** 31 - 1
|
|
|
|
class Job {
|
|
constructor(schedule, fn) {
|
|
let scheduledDate
|
|
const wrapper = () => {
|
|
const now = Date.now()
|
|
if (scheduledDate > now) {
|
|
// we're early, delay
|
|
//
|
|
// no need to check _isEnabled, we're just delaying the existing timeout
|
|
//
|
|
// see https://github.com/vatesfr/xen-orchestra/issues/4625
|
|
this._timeout = setTimeout(wrapper, scheduledDate - now)
|
|
return
|
|
}
|
|
|
|
this._isRunning = true
|
|
|
|
let result
|
|
try {
|
|
result = fn()
|
|
} catch (_) {
|
|
// catch any thrown value to ensure it does not break the job
|
|
}
|
|
let then
|
|
if (result != null && typeof (then = result.then) === 'function') {
|
|
then.call(result, scheduleNext, scheduleNext)
|
|
} else {
|
|
scheduleNext()
|
|
}
|
|
}
|
|
const scheduleNext = () => {
|
|
this._isRunning = false
|
|
|
|
if (this._isEnabled) {
|
|
const now = schedule._createDate()
|
|
scheduledDate = +next(schedule._schedule, now)
|
|
const delay = scheduledDate - now
|
|
this._timeout = delay < MAX_DELAY ? setTimeout(wrapper, delay) : setTimeout(scheduleNext, MAX_DELAY)
|
|
}
|
|
}
|
|
|
|
this._isEnabled = false
|
|
this._isRunning = false
|
|
this._scheduleNext = scheduleNext
|
|
this._timeout = undefined
|
|
}
|
|
|
|
start() {
|
|
this.stop()
|
|
|
|
this._isEnabled = true
|
|
if (!this._isRunning) {
|
|
this._scheduleNext()
|
|
}
|
|
}
|
|
|
|
stop() {
|
|
this._isEnabled = false
|
|
clearTimeout(this._timeout)
|
|
}
|
|
}
|
|
|
|
class Schedule {
|
|
constructor(pattern, zone = 'utc') {
|
|
this._schedule = parse(pattern)
|
|
this._createDate = zone.toLowerCase() === 'utc' ? moment.utc : zone === 'local' ? moment : () => moment.tz(zone)
|
|
}
|
|
|
|
createJob(fn) {
|
|
return new Job(this, fn)
|
|
}
|
|
|
|
next(n) {
|
|
const dates = new Array(n)
|
|
const schedule = this._schedule
|
|
let date = this._createDate()
|
|
for (let i = 0; i < n; ++i) {
|
|
dates[i] = (date = next(schedule, date)).toDate()
|
|
}
|
|
return dates
|
|
}
|
|
|
|
startJob(fn) {
|
|
const job = this.createJob(fn)
|
|
job.start()
|
|
return job.stop.bind(job)
|
|
}
|
|
}
|
|
|
|
const createSchedule = (...args) => new Schedule(...args)
|
|
exports.createSchedule = createSchedule
|