deno.land / x / solid@v1.5.6 / src / reactive / scheduler.ts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166// Basic port modification of Reacts Scheduler: https://github.com/facebook/react/tree/master/packages/schedulerexport interface Task { id: number; fn: ((didTimeout: boolean) => void) | null; startTime: number; expirationTime: number;}
// experimental new feature proposal stufftype NavigatorScheduling = Navigator & { scheduling: { isInputPending?: () => boolean };};
let taskIdCounter = 1, isCallbackScheduled = false, isPerformingWork = false, taskQueue: Task[] = [], currentTask: Task | null = null, shouldYieldToHost: (() => boolean) | null = null, yieldInterval = 5, deadline = 0, maxYieldInterval = 300, scheduleCallback: (() => void) | null = null, scheduledCallback: ((hasTimeRemaining: boolean, initialTime: number) => boolean) | null = null;
const maxSigned31BitInt = 1073741823;/* istanbul ignore next */function setupScheduler() { const channel = new MessageChannel(), port = channel.port2; scheduleCallback = () => port.postMessage(null); channel.port1.onmessage = () => { if (scheduledCallback !== null) { const currentTime = performance.now(); deadline = currentTime + yieldInterval; const hasTimeRemaining = true; try { const hasMoreWork = scheduledCallback(hasTimeRemaining, currentTime); if (!hasMoreWork) { scheduledCallback = null; } else port.postMessage(null); } catch (error) { // If a scheduler task throws, exit the current browser task so the // error can be observed. port.postMessage(null); throw error; } } };
if ( navigator && (navigator as NavigatorScheduling).scheduling && (navigator as NavigatorScheduling).scheduling.isInputPending ) { const scheduling = (navigator as NavigatorScheduling).scheduling; shouldYieldToHost = () => { const currentTime = performance.now(); if (currentTime >= deadline) { // There's no time left. We may want to yield control of the main // thread, so the browser can perform high priority tasks. The main ones // are painting and user input. If there's a pending paint or a pending // input, then we should yield. But if there's neither, then we can // yield less often while remaining responsive. We'll eventually yield // regardless, since there could be a pending paint that wasn't // accompanied by a call to `requestPaint`, or other main thread tasks // like network events. if (scheduling.isInputPending!()) { return true; } // There's no pending input. Only yield if we've reached the max // yield interval. return currentTime >= maxYieldInterval; } else { // There's still time left in the frame. return false; } }; } else { // `isInputPending` is not available. Since we have no way of knowing if // there's pending input, always yield at the end of the frame. shouldYieldToHost = () => performance.now() >= deadline; }}
function enqueue(taskQueue: Task[], task: Task) { function findIndex() { let m = 0; let n = taskQueue.length - 1;
while (m <= n) { const k = (n + m) >> 1; const cmp = task.expirationTime - taskQueue[k].expirationTime; if (cmp > 0) m = k + 1; else if (cmp < 0) n = k - 1; else return k; } return m; } taskQueue.splice(findIndex(), 0, task);}
export function requestCallback(fn: () => void, options?: { timeout: number }): Task { if (!scheduleCallback) setupScheduler(); let startTime = performance.now(), timeout = maxSigned31BitInt;
if (options && options.timeout) timeout = options.timeout;
const newTask: Task = { id: taskIdCounter++, fn, startTime, expirationTime: startTime + timeout };
enqueue(taskQueue, newTask); if (!isCallbackScheduled && !isPerformingWork) { isCallbackScheduled = true; scheduledCallback = flushWork; scheduleCallback!(); }
return newTask;}
export function cancelCallback(task: Task) { task.fn = null;}
function flushWork(hasTimeRemaining: boolean, initialTime: number) { // We'll need a host callback the next time work is scheduled. isCallbackScheduled = false; isPerformingWork = true; try { return workLoop(hasTimeRemaining, initialTime); } finally { currentTask = null; isPerformingWork = false; }}
function workLoop(hasTimeRemaining: boolean, initialTime: number) { let currentTime = initialTime; currentTask = taskQueue[0] || null; while (currentTask !== null) { if (currentTask.expirationTime > currentTime && (!hasTimeRemaining || shouldYieldToHost!())) { // This currentTask hasn't expired, and we've reached the deadline. break; } const callback = currentTask.fn; if (callback !== null) { currentTask.fn = null; const didUserCallbackTimeout = currentTask.expirationTime <= currentTime; callback(didUserCallbackTimeout); currentTime = performance.now(); if (currentTask === taskQueue[0]) { taskQueue.shift(); } } else taskQueue.shift(); currentTask = taskQueue[0] || null; } // Return whether there's additional work return currentTask !== null;}
Version Info