Created
March 31, 2021 20:15
-
-
Save miketromba/3f6da61de0da598737860f81c2bdae51 to your computer and use it in GitHub Desktop.
RecurringTask.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Utility for running recurring tasks | |
// Uses setInterval and setTimeout | |
// Has ability to schedule the first run instantly or wait for a fixed MS | |
export default class RecurringTask { | |
constructor({ | |
task, // can be async | |
intervalMs = 1000, // interval in milliseconds | |
lastRun = 0, // 0 == run immediately | |
// mode can be "gap" or "strict" or "hybrid" | |
// "strict" (overlap) will run every intervalMs religiously, even if that means overlapping with prior tasks that take longer than intervalMs to execute | |
// "gap" (no overlap, wait) will only schedule the next task for intervalMs AFTER the previous task has concluded. There will never be execution overlap, and the intervalMs gap will always be maintained | |
// "hybrid" (no overlap, run asap) will act like "strict" mode BUT will never overlap. If a task's execution time exceeds intervalMs, it will wait until that task completes and then immediately begin the next task. | |
mode = 'gap' // gap is the safest default for most situations | |
}) | |
{ | |
this.task = task | |
this.intervalMs = intervalMs | |
this.lastRun = lastRun | |
this.mode = mode | |
} | |
get timeUntilNextRun(){ | |
return Math.max(this.intervalMs - (Date.now() - this.lastRun), 0) | |
} | |
// Schedule the first run | |
start(){ | |
setTimeout(this.safeRunTask.bind(this), this.timeUntilNextRun) | |
} | |
// Runs right before a task is attempted | |
before(){ | |
if (this.mode == 'strict' || this.mode == 'hybrid') | |
this.lastRun = Date.now() | |
if (this.mode == 'strict') | |
setTimeout(this.safeRunTask.bind(this), this.intervalMs) | |
} | |
// Runs right after a task is attempted | |
after(){ | |
if (this.mode == 'gap'){ | |
this.lastRun = Date.now() | |
setTimeout(this.safeRunTask.bind(this), this.intervalMs) | |
} | |
if (this.mode == 'hybrid') | |
setTimeout(this.safeRunTask.bind(this), this.timeUntilNextRun) | |
} | |
async safeRunTask(){ | |
this.before() | |
try { await this.task() } | |
catch (e) { } | |
this.after() | |
} | |
} | |
// import { sleep } from './sleep' | |
// test() | |
// async function test(){ | |
// const task = new RecurringTask({ | |
// intervalMs: 1000, | |
// lastRun: 0, // Date.now(), | |
// mode: 'hybrid', | |
// async task(){ | |
// const ID = Math.round(Math.random() * 10000) | |
// console.log(`Begin task ${ID}`) | |
// await sleep(2000) | |
// console.log(`End task ${ID}`) | |
// } | |
// }) | |
// task.start() | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment