Created
February 13, 2020 05:22
-
-
Save kesne/99d25380a9e52d13665bea50bb4ee0c1 to your computer and use it in GitHub Desktop.
This file contains 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
// 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