-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbatching.ts
101 lines (86 loc) · 2.77 KB
/
batching.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { log } from './helper'
import { callPlugins } from './plugin'
import type { CallPluginOptions } from './types'
declare global {
var stateDisableBatching: boolean
}
const batching: { updates: CallPluginOptions[]; scheduler?: number } = {
updates: [],
scheduler: undefined,
}
export const scheduleUpdate = (update: CallPluginOptions) => {
batching.updates.unshift(update) // Most recent update will be processed first, to allow filtering already applied changes.
if (batching.scheduler === undefined) {
batching.scheduler = schedule(process)
}
}
// Process all batched updates.
const noDeadline = {
didTimeout: false,
timeRemaining() {
return 99999
},
}
export const batch = () => process(noDeadline)
function schedule(callback: IdleRequestCallback) {
// NOTE if window isn't present and batching isn't explicitly enabled there will be no batching.
if ((typeof window === 'undefined' && globalThis.stateDisableBatching !== false) || globalThis.stateDisableBatching === true) {
callback(noDeadline)
return
}
if (window.requestIdleCallback) {
return window.requestIdleCallback(callback)
}
// requestIdleCallback polyfill (not supported in Safari)
// https://github.com/pladaria/requestidlecallback-polyfill
// See react scheduler for better implementation.
window.requestIdleCallback =
window.requestIdleCallback ||
function idleCallbackPolyfill(innerCallback: IdleRequestCallback, _options?: IdleRequestOptions) {
const start = Date.now()
setTimeout(() => {
innerCallback({
didTimeout: false,
timeRemaining() {
return Math.max(0, 50 - (Date.now() - start))
},
})
}, 1)
return 0
}
window.cancelIdleCallback =
window.cancelIdleCallback ||
function cancelIdleCallbackPolyfill(id) {
clearTimeout(id)
}
return schedule(callback)
}
function process(deadline: IdleDeadline) {
if (batching.updates.length === 0) {
log('Trying to batch empty updates')
return
}
let shouldYield = false
let maxTries = 500
while (batching.updates.length > 0 && !shouldYield && maxTries > 0) {
maxTries -= 1
const update = batching.updates.shift()
if (update) {
callPlugins(update)
// Filter out already applied updates.
batching.updates = batching.updates.filter(
(potentialUpdate) => potentialUpdate.property !== update.property || potentialUpdate.parent !== update.parent,
)
}
// Yield current rendering cycle if out of time.
shouldYield = deadline.timeRemaining() < 1
}
if (maxTries === 0) {
log('Ran out of tries at process.', 'warning')
}
// Continuing to process in next iteration.
if (batching.updates.length > 0) {
schedule(process)
}
batching.scheduler = undefined
}