-
-
Save schickling/a07dcb8b6bb87c61c3b3f8b56af04d12 to your computer and use it in GitHub Desktop.
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
| /* eslint-disable prefer-arrow/prefer-arrow-functions */ | |
| export * from '@effect/io/Scheduler' | |
| import * as Scheduler from '@effect/io/Scheduler' | |
| /** NOTE should only be used on Main Thread */ | |
| export class BackgroundScheduler implements Scheduler.Scheduler { | |
| private running = false | |
| private taskBuckets = new Scheduler.PriorityBuckets() | |
| scheduleTask(task: Scheduler.Task, priority: number) { | |
| this.taskBuckets.scheduleTask(task, priority) | |
| if (this.running === false) { | |
| this.running = true | |
| this.starve() | |
| } | |
| } | |
| private async doWork(idleDeadline: IdleDeadline) { | |
| while (true) { | |
| const task = getNextTask(this.taskBuckets) | |
| if (task === undefined) { | |
| this.running = false | |
| return | |
| } else { | |
| task() | |
| if (navigator.scheduling!.isInputPending!() || idleDeadline.timeRemaining() <= 0.5) { | |
| this.starve() | |
| return | |
| } | |
| } | |
| } | |
| } | |
| starve() { | |
| requestIdleCallback((idleDeadline) => { | |
| this.doWork(idleDeadline) | |
| }) | |
| } | |
| } | |
| export class FallbackBackgroundScheduler implements Scheduler.Scheduler { | |
| private running = false | |
| private taskBuckets = new Scheduler.PriorityBuckets() | |
| /** Timestamp in milli seconds */ | |
| private lastWitnessedFrame = 0 | |
| private timePerFrame = 1000 / this.systemFps | |
| constructor(private systemFps = 60) { | |
| // NOTE We keep this loop running (even if no tasks might be scheduled for a while) | |
| // to optimize the latency for the next task that might be scheduled | |
| this.loopRequestAnimationFrame() | |
| } | |
| scheduleTask(task: Scheduler.Task, priority: number) { | |
| this.taskBuckets.scheduleTask(task, priority) | |
| if (this.running === false) { | |
| this.running = true | |
| this.starve() | |
| } | |
| } | |
| // TODO We should investigate a better implementation for Safari if possible | |
| private loopRequestAnimationFrame() { | |
| // TODO use `_now` from requestAnimationFrame | |
| requestAnimationFrame((_now) => { | |
| // setInterval(() => { | |
| this.lastWitnessedFrame = Date.now() | |
| this.loopRequestAnimationFrame() | |
| }) | |
| // }, this.timePerFrame) | |
| } | |
| private starve() { | |
| while (true) { | |
| const task = getNextTask(this.taskBuckets) | |
| if (task === undefined) { | |
| this.running = false | |
| return | |
| } else { | |
| task() | |
| if (this.getRemainingTimeMs() <= 0.5) { | |
| this.starve() | |
| return | |
| } | |
| } | |
| } | |
| } | |
| private getRemainingTimeMs() { | |
| const now = Date.now() | |
| const timeSinceLastFrame = now - this.lastWitnessedFrame | |
| return this.timePerFrame - timeSinceLastFrame | |
| } | |
| } | |
| /** | |
| * NOTE This implementation always prioritizes higher-priority tasks over lower priority tasks | |
| * even if the lower priority task was scheduled first | |
| */ | |
| const getNextTask = (taskBuckets: Scheduler.PriorityBuckets): Scheduler.Task | undefined => { | |
| for (const [_, tasks] of taskBuckets.buckets) { | |
| const task = tasks.shift() | |
| if (task !== undefined) return task | |
| } | |
| } | |
| export const backgroundScheduler = (): Scheduler.Scheduler => { | |
| // Safari doesn't yet support `requestIdleCallback` and `navigator.scheduling.isInputPending` | |
| // So we're falling back to a timer based scheduler | |
| if ( | |
| typeof window === 'undefined' || | |
| window.requestIdleCallback === undefined || | |
| navigator.scheduling?.isInputPending === undefined | |
| ) { | |
| return new FallbackBackgroundScheduler() | |
| } | |
| return new BackgroundScheduler() | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment