Skip to content

Commit

Permalink
♻️ Deferred → withResolvers()
Browse files Browse the repository at this point in the history
- inspired by Promise.withResolvers()
- less code than Deferred
  • Loading branch information
astoilkov committed Jan 12, 2024
1 parent e8fc2b6 commit 1da74f6
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 83 deletions.
2 changes: 1 addition & 1 deletion index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export { default as isTimeToYield } from './src/isTimeToYield'
export type { default as SchedulingPriority } from './src/SchedulingPriority'

// utility
export { default as Deferred } from './src/utils/Deferred'
export { default as queueTask } from './src/utils/queueTask'
export { default as withResolvers } from './src/utils/withResolvers'
export { default as afterFrame } from './src/utils/requestAfterFrame'
10 changes: 5 additions & 5 deletions src/state.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Task } from './tasks'
import Deferred from './utils/Deferred'
import withResolvers, { PromiseWithResolvers } from './utils/withResolvers'

type State = {
tasks: Task[]
frameTimeElapsed: boolean
onIdleCallback: Deferred
onAnimationFrame: Deferred
onIdleCallback: PromiseWithResolvers
onAnimationFrame: PromiseWithResolvers
idleDeadline: IdleDeadline | undefined
workStartTimeThisFrame: number | undefined
}
Expand All @@ -14,8 +14,8 @@ const state: State = {
tasks: [],
idleDeadline: undefined,
frameTimeElapsed: false,
onIdleCallback: new Deferred(),
onAnimationFrame: new Deferred(),
onIdleCallback: withResolvers(),
onAnimationFrame: withResolvers(),
workStartTimeThisFrame: undefined,
}

Expand Down
9 changes: 4 additions & 5 deletions src/tasks.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import state from './state'
import Deferred from './utils/Deferred'
import { startTracking } from './tracking'
import SchedulingPriority from './SchedulingPriority'
import withResolvers, { PromiseWithResolvers } from './utils/withResolvers'

export type Task = {
export type Task = PromiseWithResolvers & {
priority: SchedulingPriority
deferred: Deferred
}

/**
* Adds a task to the queue and returns the new task.
* @param priority {SchedulingPriority} The priority of the new task.
*/
export function createTask(priority: SchedulingPriority): Task {
const item = { priority, deferred: new Deferred() }
const item = { ...withResolvers(), priority }
const insertIndex =
priority === 'user-blocking'
? 0
Expand Down Expand Up @@ -56,6 +55,6 @@ export function removeTask(task: Task): void {
export function nextTask(): void {
const task = state.tasks[0]
if (task !== undefined) {
task.deferred.resolve()
task.resolve()
}
}
6 changes: 3 additions & 3 deletions src/tracking.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import state from './state'
import Deferred from './utils/Deferred'
import withResolvers from './utils/withResolvers'

let isTracking = false
let idleCallbackId: number | undefined
Expand All @@ -26,7 +26,7 @@ export function startTracking(): void {

state.onIdleCallback.resolve()

state.onIdleCallback = new Deferred()
state.onIdleCallback = withResolvers()
})
}

Expand All @@ -35,7 +35,7 @@ export function startTracking(): void {

state.onAnimationFrame.resolve()

state.onAnimationFrame = new Deferred()
state.onAnimationFrame = withResolvers()

if (state.tasks.length === 0) {
isTracking = false
Expand Down
66 changes: 0 additions & 66 deletions src/utils/Deferred.ts

This file was deleted.

21 changes: 21 additions & 0 deletions src/utils/withResolvers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export interface PromiseWithResolvers<T = void> {
promise: Promise<T>
resolve: (value: T) => void
reject: (reason?: any) => void
}

export default function withResolvers<T = void>(): PromiseWithResolvers<T> {
let resolve: (value: T) => void
let reject: () => void

const promise = new Promise<T>((res, rej) => {
resolve = res
reject = rej
})

return {
promise,
resolve: resolve!,
reject: reject!,
}
}
6 changes: 3 additions & 3 deletions src/yieldControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default async function yieldControl(
await schedule(priority)

if (state.tasks[0] !== task) {
await task.deferred
await task.promise

if (isTimeToYield(priority)) {
await schedule(priority)
Expand All @@ -50,7 +50,7 @@ export default async function yieldControl(

async function schedule(priority: SchedulingPriority): Promise<void> {
if (state.frameTimeElapsed) {
await state.onAnimationFrame
await state.onAnimationFrame.promise
}

if (
Expand All @@ -67,7 +67,7 @@ async function schedule(priority: SchedulingPriority): Promise<void> {
state.workStartTimeThisFrame = Date.now()
}
} else {
await state.onIdleCallback
await state.onIdleCallback.promise

// not checking for `navigator.scheduling?.isInputPending?.()` here because idle callbacks
// ensure no input is pending
Expand Down

0 comments on commit 1da74f6

Please sign in to comment.