Skip to content

Commit

Permalink
✨ add user-blocking priority
Browse files Browse the repository at this point in the history
this a breaking change because user-blocking actually works as the current user-visible, and user-visible now works differently by spending less time doing tasks
  • Loading branch information
astoilkov committed Nov 20, 2023
1 parent 75a8e10 commit d4e6d1c
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 11 deletions.
2 changes: 1 addition & 1 deletion src/SchedulingPriority.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
type SchedulingPriority = 'user-visible' | 'background'
type SchedulingPriority = 'user-blocking' | 'user-visible' | 'background'

export default SchedulingPriority
13 changes: 10 additions & 3 deletions src/isTimeToYield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ let lastResult = false
/**
* Determines if it's time to call `yieldControl()`.
*/
export default function isTimeToYield(priority: SchedulingPriority): boolean {
export default function isTimeToYield(priority: SchedulingPriority = 'user-visible'): boolean {
if (!hasValidContext()) {
return false
}
Expand Down Expand Up @@ -42,10 +42,17 @@ function calculateDeadline(priority: SchedulingPriority): number {
}

switch (priority) {
case 'user-visible': {
// Math.round(100 - (1000/60)) = Math.round(83,333) = 83
case 'user-blocking': {
// spent the max recommended 100ms doing 'user-blocking' tasks minus 1 frame (16ms):
// - https://developer.mozilla.org/en-US/docs/Web/Performance/How_long_is_too_long#responsiveness_goal
// - Math.round(100 - (1000/60)) = Math.round(83,333) = 83
return state.frameWorkStartTime + 83
}
case 'user-visible': {
// spent 80% percent of the frame's budget running 'user-visible' tasks:
// - Math.round((1000/60) * 0.8) = Math.round(13,333) = 13
return state.frameWorkStartTime + 4
}
case 'background': {
const idleDeadline =
state.idleDeadline === undefined
Expand Down
16 changes: 12 additions & 4 deletions src/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@ export type Task = {
export function createTask(priority: SchedulingPriority): Task {
const item = { priority, deferred: new Deferred() }
const insertIndex =
priority === 'user-visible'
priority === 'user-blocking'
? 0
: state.tasks.findIndex((task) => task.priority === 'user-visible')

state.tasks.splice(insertIndex === -1 ? 0 : insertIndex, 0, item)
: priority === 'user-visible'
? state.tasks.findIndex(
(task) => task.priority === 'user-visible' || task.priority === 'background',
)
: state.tasks.findIndex((task) => task.priority === 'background')

if (insertIndex === -1) {
state.tasks.push(item)
} else {
state.tasks.splice(insertIndex, 0, item)
}

if (state.tasks.length === 1) {
startTracking()
Expand Down
10 changes: 8 additions & 2 deletions src/yieldControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ let promiseEscapeId: number | undefined
* resolved second.
* @returns {Promise<void>} The promise that will be resolved when the queue
*/
export default async function yieldControl(priority: SchedulingPriority): Promise<void> {
export default async function yieldControl(
priority: SchedulingPriority = 'user-visible',
): Promise<void> {
if (!hasValidContext()) {
return
}
Expand Down Expand Up @@ -51,7 +53,11 @@ async function schedule(priority: SchedulingPriority): Promise<void> {
await state.onAnimationFrame
}

if (priority === 'user-visible' || typeof requestIdleCallback === 'undefined') {
if (
priority === 'user-visible' ||
priority === 'user-blocking' ||
typeof requestIdleCallback === 'undefined'
) {
await new Promise<void>((resolve) => queueTask(resolve))

// istanbul ignore if
Expand Down
4 changes: 3 additions & 1 deletion src/yieldOrContinue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import SchedulingPriority from './SchedulingPriority'

// disabling ESLint otherwise `requestPromiseEscape()` in `yieldControl()` won't work
// eslint-disable-next-line @typescript-eslint/promise-function-async
export default function yieldOrContinue(priority: SchedulingPriority): Promise<void> {
export default function yieldOrContinue(
priority: SchedulingPriority = 'user-visible',
): Promise<void> {
if (isTimeToYield(priority)) {
return yieldControl(priority)
}
Expand Down

0 comments on commit d4e6d1c

Please sign in to comment.