Skip to content

Commit a8785b6

Browse files
committed
timer: add ETimer.IDLE + support passing native arguments in the tail of Timer callback
Signed-off-by: 🕷️ <[email protected]>
1 parent 30db311 commit a8785b6

File tree

3 files changed

+57
-10
lines changed

3 files changed

+57
-10
lines changed

src/api/time.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export type TTaskPriority =
9090
export enum ETimer {
9191
TIMEOUT,
9292
ANIMATION,
93+
IDLE,
9394
TASK,
9495
}
9596
type TTimerMeasurable = {
@@ -103,6 +104,10 @@ type TTimerTimeout = TTimerMeasurable & {
103104
type TTimerAnimation = TTimerMeasurable & {
104105
type: ETimer.ANIMATION;
105106
};
107+
type TTimerIdle = TTimerMeasurable & {
108+
type: ETimer.IDLE;
109+
delay: number;
110+
};
106111
type TTimerTask = TTimerMeasurable & {
107112
type: ETimer.TASK;
108113
delay: number;
@@ -111,6 +116,7 @@ type TTimerTask = TTimerMeasurable & {
111116
type TTimerOptions =
112117
| TTimerTimeout
113118
| TTimerAnimation
119+
| TTimerIdle
114120
| TTimerTask;
115121

116122
/**
@@ -133,6 +139,7 @@ export class Timer {
133139

134140
if (
135141
this.#options.type === ETimer.TIMEOUT ||
142+
this.#options.type === ETimer.IDLE ||
136143
this.#options.type === ETimer.TASK
137144
) {
138145
this.delay = this.#options.delay;
@@ -158,10 +165,15 @@ export class Timer {
158165
} else if (
159166
this.#options.type === ETimer.ANIMATION
160167
) {
161-
this.#handler = requestAnimationFrame(() => {
168+
this.#handler = requestAnimationFrame((...argsAF) => {
162169
this.#handler = 0;
163-
this.trigger(...args);
170+
this.trigger(...[...args, ...argsAF]);
164171
});
172+
} else if (this.#options.type === ETimer.IDLE) {
173+
this.#handler = requestIdleCallback((...argsIC) => {
174+
this.#handler = 0;
175+
this.trigger(...[...args, ...argsIC]);
176+
}, { timeout: this.delay });
165177
} else if (this.#options.type === ETimer.TASK) {
166178
this.#abortController = new AbortController();
167179
nativePostTask(() => {
@@ -196,6 +208,9 @@ export class Timer {
196208
} else if (this.#options.type === ETimer.ANIMATION) {
197209
this.#handler && cancelAnimationFrame(this.#handler);
198210
this.#handler = 0;
211+
} else if (this.#options.type === ETimer.IDLE) {
212+
this.#handler && cancelIdleCallback(this.#handler);
213+
this.#handler = 0;
199214
} else if (this.#options.type === ETimer.TASK) {
200215
this.#abortController && this.#abortController.abort();
201216
this.#abortController = null;

tests/browserPolyfill.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Object.assign(globalThis, {
1717
) {
1818
return setTimeout(() => {
1919
callback(performance.now());
20-
}, 1e3 / 60);
20+
}, 0);
2121
},
2222

2323
cancelAnimationFrame: function cancelAnimationFrameDumbStub(id: number) {

tests/time_test.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,25 +112,57 @@ describe('Timer - animation + measurable', () => {
112112
test('start/stop', async () => {
113113
let count = 0;
114114
const TIMESPAN = 100;
115+
let timerArg = 0;
116+
const timerArgExpected = 100;
117+
let hasAnimationArgs = false;
115118
const animation = new Timer({
116119
type: ETimer.ANIMATION,
117120
measurable: true,
118-
}, () => {
121+
}, (_timerArg: number, time: DOMHighResTimeStamp) => {
119122
count++;
120-
animation.start();
123+
timerArg = _timerArg;
124+
hasAnimationArgs = !!time && typeof time === 'number';
121125
});
122126

123127
expect(animation.isPending()).toBe(false);
124-
animation.start();
128+
animation.start(timerArgExpected);
125129
expect(animation.isPending()).toBe(true);
126130

127131
await wait(TIMESPAN);
128132

129-
animation.stop();
133+
expect(hasAnimationArgs).toBe(true);
134+
expect(timerArg).toBe(timerArgExpected);
130135
expect(animation.isPending()).toBe(false);
131-
expect(animation.callbackSelfTime).toBeLessThan(0.5);
132-
expect(count).toBeGreaterThan(1);
133-
expect(count).toBeLessThan(TIMESPAN / (1e3 / 60));
136+
expect(animation.callbackSelfTime > 0).toBe(true);
137+
expect(count).toBe(1);
138+
});
139+
});
140+
141+
describe('Timer - idle', () => {
142+
test('start/stop', async () => {
143+
let counter = 0;
144+
let timerArg = 0;
145+
const timerArgExpected = 100;
146+
let hasIdleArgs = false;
147+
const task = new Timer(
148+
{ type: ETimer.IDLE, delay: DELAY },
149+
(_timerArg: number, deadline: IdleDeadline) => {
150+
counter++;
151+
timerArg = _timerArg;
152+
hasIdleArgs = !!deadline && typeof deadline.didTimeout == 'boolean';
153+
task.start();
154+
},
155+
);
156+
157+
task.start(timerArgExpected);
158+
expect(task.isPending()).toBe(true);
159+
await wait(DELAY + DELAY / 2);
160+
task.stop();
161+
162+
expect(timerArg).toBe(timerArgExpected);
163+
expect(hasIdleArgs).toBe(true);
164+
expect(task.isPending()).toBe(false);
165+
expect(counter).toBe(1);
134166
});
135167
});
136168

0 commit comments

Comments
 (0)