Skip to content

Instantly share code, notes, and snippets.

@kesne
Created February 13, 2020 05:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kesne/99d25380a9e52d13665bea50bb4ee0c1 to your computer and use it in GitHub Desktop.
Save kesne/99d25380a9e52d13665bea50bb4ee0c1 to your computer and use it in GitHub Desktop.
// We have two types of work, A, B. We schedule A to be done very frequently!
// It doesn't take a lot of time (maybe a ms or two), but B can take a decent
// amount of time (maybe 10ms+).
//
// Write a scheduler that can perform tasks A and B as often as possible, BUT
// does not result in any frame skipping (do not lock the main thread for very
// long).
// MOCK TO MAKE NODE WORK:
if (typeof requestAnimationFrame === "undefined") {
// @ts-ignore
requestAnimationFrame = cb => setTimeout(cb, 0);
}
type TaskFn = () => void;
type Task = {
estimate?: number;
fn: TaskFn;
};
class Scheduler {
private declare frameBudget: number;
private declare queue: Task[];
private declare started: boolean;
constructor(frameBudget: number = 16) {
this.frameBudget = frameBudget;
this.queue = [];
this.started = false;
}
add(task: Task) {
const wasEmpty = this.queue.length;
this.queue.push(task);
if (this.started && wasEmpty) {
requestAnimationFrame(() => this.performWork());
}
}
start() {
this.started = true;
requestAnimationFrame(() => this.performWork());
}
private performWork() {
const start = Date.now();
while (start + this.frameBudget > Date.now()) {
// No work, just bail:
if (!this.queue.length) {
break;
}
if (this.queue[0].estimate > start + this.frameBudget - Date.now()) {
break;
}
const task = this.queue.shift();
task.fn();
}
if (Date.now() > start + this.frameBudget) {
console.log(
"\n\n\nEXCEEDED FRAME BUDGET\n\n\n\n\n\n\n",
Date.now() - (start + this.frameBudget)
);
}
if (this.queue.length) {
requestAnimationFrame(() => this.performWork());
}
}
}
const scheduler = new Scheduler();
const taskA = {
estimate: 1,
fn() {
console.log("A");
// Lock the main thread for 1ms to simulate work:
const start = Date.now();
while (start + 1 > Date.now()) {}
}
};
const taskB = {
estimate: 10,
fn() {
console.log("B");
// Lock the main thread for 1ms to simulate work:
const start = Date.now();
while (start + 10 > Date.now()) {}
}
};
for (var i = 0; i < 1000; i++) {
scheduler.add(Math.random() > 0.5 ? taskA : taskB);
}
scheduler.start();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment