@oscarpalmer/timer 0.43.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.mts +6 -19
- package/dist/constants.mjs +5 -13
- package/dist/global.d.mts +4 -2
- package/dist/global.mjs +5 -14
- package/dist/index.d.mts +52 -95
- package/dist/index.mjs +232 -411
- package/dist/is.d.mts +1 -3
- package/dist/misc.d.mts +10 -0
- package/dist/misc.mjs +34 -0
- package/dist/models.d.mts +110 -7
- package/dist/repeat.d.mts +3 -3
- package/dist/repeat.mjs +3 -3
- package/dist/timer.d.mts +3 -50
- package/dist/timer.mjs +50 -94
- package/dist/wait.d.mts +3 -2
- package/dist/wait.mjs +3 -3
- package/dist/when.d.mts +3 -46
- package/dist/when.mjs +77 -117
- package/dist/work.d.mts +3 -5
- package/dist/work.mjs +26 -31
- package/package.json +8 -8
- package/src/constants.ts +8 -19
- package/src/global.ts +12 -22
- package/src/index.ts +4 -5
- package/src/is.ts +1 -2
- package/src/misc.ts +49 -0
- package/src/models.ts +129 -6
- package/src/repeat.ts +4 -6
- package/src/timer.ts +60 -130
- package/src/wait.ts +4 -6
- package/src/when.ts +98 -153
- package/src/work.ts +33 -57
- package/dist/delay.d.mts +0 -3
- package/dist/delay.mjs +0 -2
- package/dist/get.d.mts +0 -8
- package/dist/get.mjs +0 -15
- package/src/delay.ts +0 -2
- package/src/get.ts +0 -17
package/dist/when.mjs
CHANGED
|
@@ -1,124 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getValidNumber, getValidTimeout } from "./
|
|
1
|
+
import { MESSAGE_STARTED, TYPE_WHEN, WORK_CONTINUE, WORK_PAUSE, WORK_STOP } from "./constants.mjs";
|
|
2
|
+
import { getValidNumber, getValidTimeout } from "./misc.mjs";
|
|
3
3
|
import "./global.mjs";
|
|
4
4
|
import { TimerTrace } from "./models.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { createTimer } from "./timer.mjs";
|
|
6
6
|
import { noop } from "@oscarpalmer/atoms/function";
|
|
7
7
|
//#region src/when.ts
|
|
8
|
-
|
|
9
|
-
state
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return this.state.timer?.active ?? false;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Is the timer destroyed?
|
|
24
|
-
*/
|
|
25
|
-
get destroyed() {
|
|
26
|
-
return this.state.timer == null;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Is the timer paused?
|
|
30
|
-
*/
|
|
31
|
-
get paused() {
|
|
32
|
-
return this.state.timer?.paused ?? false;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Get the timer's origin _(if debugging is enabled)_
|
|
36
|
-
*/
|
|
37
|
-
get trace() {
|
|
38
|
-
return globalThis._oscarpalmer_timer_debug ?? false ? this.state.timer?.trace : void 0;
|
|
39
|
-
}
|
|
40
|
-
constructor(condition, options) {
|
|
41
|
-
Object.defineProperty(this, "$timer", { value: TYPE_WHEN });
|
|
42
|
-
const { state } = this;
|
|
43
|
-
state.promise = new Promise((resolve, reject) => {
|
|
44
|
-
state.resolver = resolve;
|
|
45
|
-
state.rejecter = reject;
|
|
46
|
-
});
|
|
47
|
-
let result = false;
|
|
48
|
-
this.state.timer = new Timer(TYPE_WHEN, {
|
|
49
|
-
callback() {
|
|
50
|
-
try {
|
|
51
|
-
if (condition()) {
|
|
52
|
-
result = true;
|
|
53
|
-
state.timer.stop();
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
state.timer.stop();
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
trace: new TimerTrace().stack
|
|
60
|
-
}, {
|
|
61
|
-
onAfter: () => {
|
|
62
|
-
if (result) state.resolver?.();
|
|
63
|
-
else state.rejecter?.();
|
|
64
|
-
this.destroy();
|
|
65
|
-
},
|
|
66
|
-
onError: () => {
|
|
67
|
-
state.rejecter?.();
|
|
68
|
-
this.destroy();
|
|
69
|
-
},
|
|
70
|
-
count: getValidNumber(options?.count),
|
|
71
|
-
interval: getValidNumber(options?.interval),
|
|
72
|
-
timeout: getValidTimeout(options?.timeout)
|
|
73
|
-
}, false);
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Continues the timer _(if it was paused)_
|
|
77
|
-
*/
|
|
78
|
-
continue() {
|
|
79
|
-
this.state.timer?.continue();
|
|
80
|
-
return this;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Destroys the timer _(and stops it,if it was running)_
|
|
84
|
-
*/
|
|
85
|
-
destroy() {
|
|
86
|
-
const { state } = this;
|
|
87
|
-
state.timer?.destroy();
|
|
88
|
-
state.promise = void 0;
|
|
89
|
-
state.resolver = noop;
|
|
90
|
-
state.rejecter = noop;
|
|
91
|
-
state.timer = void 0;
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Pauses the timer _(if it was running)_
|
|
95
|
-
*/
|
|
96
|
-
pause() {
|
|
97
|
-
this.state.timer?.pause();
|
|
98
|
-
return this;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Start the timer
|
|
102
|
-
*
|
|
103
|
-
* @param resolve Optional resolve callback
|
|
104
|
-
* @returns Promise that resolves when the condition is met
|
|
105
|
-
*/
|
|
106
|
-
start(resolve) {
|
|
107
|
-
const { state } = this;
|
|
108
|
-
if (state.timer == null) throw new Error(MESSAGE_DESTROYED);
|
|
109
|
-
if (state.started) throw new Error(MESSAGE_STARTED);
|
|
110
|
-
state.started = true;
|
|
111
|
-
state.timer.start();
|
|
112
|
-
return state.promise.then(resolve);
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Stops the timer _(if it was running)_
|
|
116
|
-
*/
|
|
117
|
-
stop() {
|
|
118
|
-
this.state.timer?.stop();
|
|
119
|
-
return this;
|
|
8
|
+
function onAfter(state) {
|
|
9
|
+
if (state.result) state.resolver?.();
|
|
10
|
+
else state.rejecter?.();
|
|
11
|
+
}
|
|
12
|
+
function onCallback(condition, state) {
|
|
13
|
+
try {
|
|
14
|
+
if (condition()) {
|
|
15
|
+
state.result = true;
|
|
16
|
+
state.timer.stop();
|
|
17
|
+
}
|
|
18
|
+
} catch {
|
|
19
|
+
state.timer.stop();
|
|
120
20
|
}
|
|
121
|
-
}
|
|
21
|
+
}
|
|
22
|
+
function onWhen(type, instance, state) {
|
|
23
|
+
state.timer?.[type]?.();
|
|
24
|
+
return instance;
|
|
25
|
+
}
|
|
26
|
+
function startWhen(state, resolve) {
|
|
27
|
+
if (state.started) throw new Error(MESSAGE_STARTED);
|
|
28
|
+
state.started = true;
|
|
29
|
+
state.timer.start();
|
|
30
|
+
return state.promise.then(resolve);
|
|
31
|
+
}
|
|
122
32
|
/**
|
|
123
33
|
* Create a conditional timer
|
|
124
34
|
* @param condition Condition to check
|
|
@@ -126,7 +36,57 @@ var When = class {
|
|
|
126
36
|
* @returns Timer instance
|
|
127
37
|
*/
|
|
128
38
|
function when(condition, options) {
|
|
129
|
-
|
|
39
|
+
const state = {
|
|
40
|
+
promise: void 0,
|
|
41
|
+
result: false,
|
|
42
|
+
started: false,
|
|
43
|
+
timer: void 0
|
|
44
|
+
};
|
|
45
|
+
let instance;
|
|
46
|
+
state.promise = new Promise((resolve, reject) => {
|
|
47
|
+
state.resolver = resolve;
|
|
48
|
+
state.rejecter = reject;
|
|
49
|
+
});
|
|
50
|
+
state.timer = createTimer(TYPE_WHEN, {
|
|
51
|
+
callback: () => onCallback(condition, state),
|
|
52
|
+
trace: new TimerTrace().stack
|
|
53
|
+
}, {
|
|
54
|
+
onAfter: () => onAfter(state),
|
|
55
|
+
onError: () => state.rejecter?.(),
|
|
56
|
+
count: getValidNumber(options?.count),
|
|
57
|
+
interval: getValidNumber(options?.interval),
|
|
58
|
+
timeout: getValidTimeout(options?.timeout)
|
|
59
|
+
}, false);
|
|
60
|
+
instance = {
|
|
61
|
+
continue: () => onWhen(WORK_CONTINUE, instance, state),
|
|
62
|
+
destroy: noop,
|
|
63
|
+
pause: () => onWhen(WORK_PAUSE, instance, state),
|
|
64
|
+
start: (resolve) => startWhen(state, resolve),
|
|
65
|
+
stop: () => onWhen(WORK_STOP, instance, state)
|
|
66
|
+
};
|
|
67
|
+
Object.defineProperties(instance, {
|
|
68
|
+
$timer: {
|
|
69
|
+
enumerable: false,
|
|
70
|
+
value: TYPE_WHEN
|
|
71
|
+
},
|
|
72
|
+
active: {
|
|
73
|
+
enumerable: true,
|
|
74
|
+
get: () => state.timer.active
|
|
75
|
+
},
|
|
76
|
+
destroyed: {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
value: false
|
|
79
|
+
},
|
|
80
|
+
paused: {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
get: () => state.timer.paused
|
|
83
|
+
},
|
|
84
|
+
trace: {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
get: () => globalThis._oscarpalmer_timer_debug ?? false ? state.timer?.trace : void 0
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return Object.freeze(instance);
|
|
130
90
|
}
|
|
131
91
|
//#endregion
|
|
132
92
|
export { when };
|
package/dist/work.d.mts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { Timer } from "./
|
|
2
|
-
import { TimerOptions, TimerState, WorkHandlerTimer, WorkHandlerType } from "./models.mjs";
|
|
3
|
-
|
|
1
|
+
import { Timer, TimerState, WorkHandlerType } from "./models.mjs";
|
|
4
2
|
//#region src/work.d.ts
|
|
5
|
-
declare function stop(
|
|
6
|
-
declare function work(type: WorkHandlerType,
|
|
3
|
+
declare function stop(state: TimerState): Timer;
|
|
4
|
+
declare function work(type: WorkHandlerType, state: TimerState, hide?: boolean): Timer;
|
|
7
5
|
//#endregion
|
|
8
6
|
export { stop, work };
|
package/dist/work.mjs
CHANGED
|
@@ -1,25 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { WORK_PAUSE } from "./constants.mjs";
|
|
2
|
+
import { updateStates } from "./misc.mjs";
|
|
2
3
|
//#region src/work.ts
|
|
3
|
-
function finish(
|
|
4
|
+
function finish(state, success) {
|
|
5
|
+
updateStates(state);
|
|
4
6
|
cancelAnimationFrame(state.frame);
|
|
5
|
-
TIMERS_ACTIVE.delete(timer.instance);
|
|
6
7
|
state.active = false;
|
|
7
8
|
state.elapsed = 0;
|
|
8
9
|
state.frame = void 0;
|
|
9
|
-
if (
|
|
10
|
-
else options.onAfter?.(success);
|
|
10
|
+
if (state.name === "wait") state.callback();
|
|
11
|
+
else state.options.onAfter?.(success);
|
|
11
12
|
}
|
|
12
13
|
function ignore(type, state) {
|
|
13
|
-
if (state.destroyed) return type !== WORK_STOP;
|
|
14
14
|
if (state.paused) return type === "pause" || type === "start";
|
|
15
15
|
return state.active && type === "start";
|
|
16
16
|
}
|
|
17
|
-
function
|
|
18
|
-
cancelAnimationFrame(state.frame);
|
|
19
|
-
state.frame = void 0;
|
|
20
|
-
return timer.instance;
|
|
21
|
-
}
|
|
22
|
-
function run(timer, state, options) {
|
|
17
|
+
function run(state) {
|
|
23
18
|
let last;
|
|
24
19
|
let start;
|
|
25
20
|
return function step(now) {
|
|
@@ -30,18 +25,18 @@ function run(timer, state, options) {
|
|
|
30
25
|
state.elapsed += difference;
|
|
31
26
|
state.total += difference;
|
|
32
27
|
last = now;
|
|
33
|
-
if (options.timeout > 0 && state.total >= options.timeout) {
|
|
34
|
-
options.onError?.();
|
|
35
|
-
finish(
|
|
28
|
+
if (state.options.timeout > 0 && state.total >= state.options.timeout) {
|
|
29
|
+
state.options.onError?.();
|
|
30
|
+
finish(state, false);
|
|
36
31
|
return;
|
|
37
32
|
}
|
|
38
|
-
if (options.interval === 0 || state.elapsed >= options.interval - 5) {
|
|
39
|
-
if (options.count > -1) state.callback(state.index);
|
|
33
|
+
if (state.options.interval === 0 || state.elapsed >= state.options.interval - 5) {
|
|
34
|
+
if (state.options.count > -1) state.callback(state.index);
|
|
40
35
|
start = now;
|
|
41
36
|
state.elapsed = 0;
|
|
42
37
|
state.index += 1;
|
|
43
|
-
if (options.count === -1 || options.count > 0 && state.index >= options.count) {
|
|
44
|
-
finish(
|
|
38
|
+
if (state.options.count === -1 || state.options.count > 0 && state.index >= state.options.count) {
|
|
39
|
+
finish(state, true);
|
|
45
40
|
return;
|
|
46
41
|
}
|
|
47
42
|
}
|
|
@@ -54,30 +49,30 @@ function setState(type, state) {
|
|
|
54
49
|
state.index = pausable ? state.index : 0;
|
|
55
50
|
state.total = pausable ? state.total : 0;
|
|
56
51
|
}
|
|
57
|
-
function stop(
|
|
52
|
+
function stop(state) {
|
|
53
|
+
updateStates(state);
|
|
58
54
|
cancelAnimationFrame(state.frame);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
state.active = !stop;
|
|
55
|
+
state.options.onAfter?.(false);
|
|
56
|
+
state.active = false;
|
|
62
57
|
state.frame = void 0;
|
|
63
58
|
state.paused = false;
|
|
64
|
-
return timer
|
|
59
|
+
return state.timer;
|
|
65
60
|
}
|
|
66
|
-
function work(type,
|
|
67
|
-
if (ignore(type, state)) return timer
|
|
61
|
+
function work(type, state, hide) {
|
|
62
|
+
if (ignore(type, state)) return state.timer;
|
|
68
63
|
setState(type, state);
|
|
69
|
-
if (type === "stop") return stop(
|
|
64
|
+
if (type === "stop") return stop(state);
|
|
70
65
|
if (type === "pause" || type === "restart") {
|
|
71
66
|
cancelAnimationFrame(state.frame);
|
|
72
67
|
state.frame = void 0;
|
|
73
68
|
}
|
|
74
69
|
state.active = true;
|
|
75
70
|
state.paused = type === WORK_PAUSE;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const runner = run(
|
|
71
|
+
updateStates(state, state.paused ? hide ?? false ? "hidden" : void 0 : "active");
|
|
72
|
+
if (state.paused) return state.timer;
|
|
73
|
+
const runner = run(state);
|
|
79
74
|
state.frame = requestAnimationFrame(runner);
|
|
80
|
-
return timer
|
|
75
|
+
return state.timer;
|
|
81
76
|
}
|
|
82
77
|
//#endregion
|
|
83
78
|
export { stop, work };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oscarpalmer/timer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
4
4
|
"description": "A better solution for timeout- and interval-based timers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"requestAnimationFrame",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"types": "./dist/index.d.mts",
|
|
31
31
|
"default": "./dist/index.mjs"
|
|
32
32
|
},
|
|
33
|
-
"./
|
|
34
|
-
"types": "./dist/
|
|
35
|
-
"default": "./dist/
|
|
33
|
+
"./models": {
|
|
34
|
+
"types": "./dist/models.d.mts",
|
|
35
|
+
"default": "./dist/models.mjs"
|
|
36
36
|
},
|
|
37
37
|
"./repeat": {
|
|
38
38
|
"types": "./dist/repeat.d.mts",
|
|
@@ -56,13 +56,13 @@
|
|
|
56
56
|
"watch": "npx vite build --watch"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@oscarpalmer/atoms": "^0.
|
|
59
|
+
"@oscarpalmer/atoms": "^0.193"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@oxlint/plugins": "^1.
|
|
63
|
-
"@types/node": "^
|
|
62
|
+
"@oxlint/plugins": "^1.80",
|
|
63
|
+
"@types/node": "^26.4",
|
|
64
64
|
"@vitest/coverage-istanbul": "^4.1",
|
|
65
|
-
"jsdom": "^
|
|
65
|
+
"jsdom": "^30",
|
|
66
66
|
"tsdown": "^0.22",
|
|
67
67
|
"typescript": "^6",
|
|
68
68
|
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
package/src/constants.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {Timer} from './timer';
|
|
1
|
+
import type {TimerName, TimerStates, WorkHandlerType} from './models';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Buffer value to use when evaluating if a specific time is within a certain range
|
|
@@ -8,31 +7,21 @@ export const BUFFER_INTERVAL = 5;
|
|
|
8
7
|
|
|
9
8
|
export const DEFAULT_TIMEOUT = 30_000;
|
|
10
9
|
|
|
11
|
-
/**
|
|
12
|
-
* Message to show when a when-timer is destroyed
|
|
13
|
-
*/
|
|
14
|
-
export const MESSAGE_DESTROYED = 'Timer has already been destroyed';
|
|
15
|
-
|
|
16
10
|
/**
|
|
17
11
|
* Message to show when a when-timer is started
|
|
18
12
|
*/
|
|
19
13
|
export const MESSAGE_STARTED = 'Timer has already been started';
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* A set of timers that were paused due to the document being hidden
|
|
28
|
-
*/
|
|
29
|
-
export const TIMERS_HIDDEN = new Set<Timer>();
|
|
15
|
+
export const STATES: TimerStates = {
|
|
16
|
+
active: new Set(),
|
|
17
|
+
hidden: new Set(),
|
|
18
|
+
};
|
|
30
19
|
|
|
31
|
-
export const TYPE_REPEAT:
|
|
20
|
+
export const TYPE_REPEAT: TimerName = 'repeat';
|
|
32
21
|
|
|
33
|
-
export const TYPE_WAIT:
|
|
22
|
+
export const TYPE_WAIT: TimerName = 'wait';
|
|
34
23
|
|
|
35
|
-
export const TYPE_WHEN:
|
|
24
|
+
export const TYPE_WHEN: TimerName = 'when';
|
|
36
25
|
|
|
37
26
|
export const WORK_CONTINUE: WorkHandlerType = 'continue';
|
|
38
27
|
|
package/src/global.ts
CHANGED
|
@@ -1,29 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import {STATES} from './constants';
|
|
2
|
+
import {onVisibilityChange} from './misc';
|
|
3
|
+
import type {Timer} from './models';
|
|
3
4
|
|
|
4
5
|
declare global {
|
|
5
6
|
var _oscarpalmer_timer_debug: boolean | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* All active timers _(or `undefined` if debugging is not enabled)_
|
|
9
|
+
*/
|
|
6
10
|
var _oscarpalmer_timers: Timer[] | undefined;
|
|
7
11
|
}
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
},
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/* istanbul ignore next */
|
|
18
|
-
document.addEventListener('visibilitychange', () => {
|
|
19
|
-
const from = document.hidden ? TIMERS_ACTIVE : TIMERS_HIDDEN;
|
|
20
|
-
const method = document.hidden ? WORK_PAUSE : WORK_CONTINUE;
|
|
21
|
-
const to = document.hidden ? TIMERS_HIDDEN : TIMERS_ACTIVE;
|
|
22
|
-
|
|
23
|
-
for (const timer of from) {
|
|
24
|
-
timer[method]();
|
|
25
|
-
to.add(timer);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
from.clear();
|
|
13
|
+
Object.defineProperty(globalThis, '_oscarpalmer_timers', {
|
|
14
|
+
get() {
|
|
15
|
+
return globalThis._oscarpalmer_timer_debug ? [...STATES.active].map(state => state.timer) : [];
|
|
16
|
+
},
|
|
29
17
|
});
|
|
18
|
+
|
|
19
|
+
document.addEventListener('visibilitychange', onVisibilityChange);
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import './global';
|
|
2
2
|
|
|
3
|
-
export * from './delay';
|
|
4
3
|
export {isRepeated, isTimer, isWaited, isWhen} from './is';
|
|
5
|
-
export
|
|
6
|
-
export
|
|
7
|
-
export
|
|
8
|
-
export
|
|
4
|
+
export type {RepeatOptions, Timer, TimerOptions, When, WhenOptions} from './models';
|
|
5
|
+
export {repeat} from './repeat';
|
|
6
|
+
export {wait} from './wait';
|
|
7
|
+
export {when} from './when';
|
package/src/is.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type {PlainObject} from '@oscarpalmer/atoms/models';
|
|
2
2
|
import {TYPE_REPEAT, TYPE_WAIT, TYPE_WHEN} from './constants';
|
|
3
|
-
import type {Timer} from './
|
|
4
|
-
import type {When} from './when';
|
|
3
|
+
import type {Timer, When} from './models';
|
|
5
4
|
|
|
6
5
|
function is(names: string[], value: unknown) {
|
|
7
6
|
return names.includes((value as PlainObject)?.$timer as string);
|
package/src/misc.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {noop} from '@oscarpalmer/atoms/function';
|
|
2
|
+
import type {GenericCallback} from '@oscarpalmer/atoms/models';
|
|
3
|
+
import {DEFAULT_TIMEOUT, STATES, WORK_CONTINUE, WORK_PAUSE} from './constants';
|
|
4
|
+
import type {TimerStates, TimerState} from './models';
|
|
5
|
+
import {work} from './work';
|
|
6
|
+
|
|
7
|
+
export function getCallback(value: unknown): GenericCallback {
|
|
8
|
+
return typeof value === 'function' ? (value as GenericCallback) : noop;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getValidNumber(value: unknown, defaultValue?: number): number {
|
|
12
|
+
const actualDefault = defaultValue ?? 0;
|
|
13
|
+
|
|
14
|
+
return typeof value === 'number' && value > actualDefault ? value : actualDefault;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getValidTimeout(value: unknown): number {
|
|
18
|
+
return typeof value === 'number' && value > 0 ? value : DEFAULT_TIMEOUT;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/* istanbul ignore next */
|
|
22
|
+
export function onVisibilityChange(): void {
|
|
23
|
+
const from = document.hidden ? STATES.active : STATES.hidden;
|
|
24
|
+
const type = document.hidden ? WORK_PAUSE : WORK_CONTINUE;
|
|
25
|
+
|
|
26
|
+
for (const stored of from) {
|
|
27
|
+
const state = stored instanceof WeakRef ? stored.deref() : stored;
|
|
28
|
+
|
|
29
|
+
if (state != null) {
|
|
30
|
+
work(type, state, true);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function updateStates(state: TimerState, key?: keyof TimerStates): void {
|
|
36
|
+
STATES.active.delete(state);
|
|
37
|
+
|
|
38
|
+
const hidden = [...STATES.hidden].find(stored => stored.deref() === state);
|
|
39
|
+
|
|
40
|
+
/* istanbul ignore next */
|
|
41
|
+
if (hidden != null) {
|
|
42
|
+
STATES.hidden.delete(hidden);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (key != null) {
|
|
46
|
+
/* istanbul ignore next */
|
|
47
|
+
STATES[key].add((key === 'hidden' ? new WeakRef(state) : state) as never);
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/models.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import type {Timer} from './timer';
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Options for a repeating timer
|
|
5
3
|
*/
|
|
@@ -26,6 +24,64 @@ export type RepeatOptions = {
|
|
|
26
24
|
timeout: number;
|
|
27
25
|
};
|
|
28
26
|
|
|
27
|
+
export type Timer = {
|
|
28
|
+
/**
|
|
29
|
+
* Is the timer active?
|
|
30
|
+
*/
|
|
31
|
+
get active(): boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Is the timer destroyed?
|
|
35
|
+
*
|
|
36
|
+
* @deprecated Timers take care of their own cleanup; this always returns `false`
|
|
37
|
+
*/
|
|
38
|
+
get destroyed(): boolean;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Is the timer paused?
|
|
42
|
+
*/
|
|
43
|
+
get paused(): boolean;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the timer's origin _(if debugging is enabled)_
|
|
47
|
+
*/
|
|
48
|
+
get trace(): string | undefined;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Continue running the timer _(if it's paused)_
|
|
52
|
+
*/
|
|
53
|
+
continue(): Timer;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Destroy the timer
|
|
57
|
+
*
|
|
58
|
+
* @deprecated Timers take care of their own cleanup
|
|
59
|
+
*/
|
|
60
|
+
destroy(): void;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Pause the timer _(if it's running)_
|
|
64
|
+
*/
|
|
65
|
+
pause(): Timer;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Restart the timer _(or start it, if it's not running)_
|
|
69
|
+
*/
|
|
70
|
+
restart(): Timer;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Start the timer _(if it's not running)_
|
|
74
|
+
*/
|
|
75
|
+
start(): Timer;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Stop the timer _(if it's running)_
|
|
79
|
+
*/
|
|
80
|
+
stop(): Timer;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export type TimerName = 'repeat' | 'wait' | 'when';
|
|
84
|
+
|
|
29
85
|
export type TimerOptions = {
|
|
30
86
|
onAfter: ((finished: boolean) => void) | undefined;
|
|
31
87
|
onError: (() => void) | undefined;
|
|
@@ -39,11 +95,25 @@ export type TimerState = {
|
|
|
39
95
|
callback: () => void;
|
|
40
96
|
destroyed: boolean;
|
|
41
97
|
elapsed: number;
|
|
42
|
-
frame
|
|
98
|
+
frame?: number;
|
|
43
99
|
index: number;
|
|
100
|
+
name: TimerName;
|
|
101
|
+
options: TimerOptions;
|
|
44
102
|
paused: boolean;
|
|
103
|
+
timer: Timer;
|
|
45
104
|
total: number;
|
|
46
|
-
trace
|
|
105
|
+
trace?: string;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export type TimerStates = {
|
|
109
|
+
/**
|
|
110
|
+
* A set of all active timers
|
|
111
|
+
*/
|
|
112
|
+
active: Set<TimerState>;
|
|
113
|
+
/**
|
|
114
|
+
* A set of timers that were paused due to the document being hidden
|
|
115
|
+
*/
|
|
116
|
+
hidden: Set<WeakRef<TimerState>>;
|
|
47
117
|
};
|
|
48
118
|
|
|
49
119
|
export class TimerTrace extends Error {
|
|
@@ -54,7 +124,59 @@ export class TimerTrace extends Error {
|
|
|
54
124
|
}
|
|
55
125
|
}
|
|
56
126
|
|
|
57
|
-
export type
|
|
127
|
+
export type When = {
|
|
128
|
+
/**
|
|
129
|
+
* Is the timer active?
|
|
130
|
+
*/
|
|
131
|
+
get active(): boolean;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Is the timer destroyed?
|
|
135
|
+
*
|
|
136
|
+
* @deprecated Timers take care of their own cleanup; this always returns `false`
|
|
137
|
+
*/
|
|
138
|
+
get destroyed(): boolean;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Is the timer paused?
|
|
142
|
+
*/
|
|
143
|
+
get paused(): boolean;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get the timer's origin _(if debugging is enabled)_
|
|
147
|
+
*/
|
|
148
|
+
get trace(): string | undefined;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Continues the timer _(if it was paused)_
|
|
152
|
+
*/
|
|
153
|
+
continue(): When;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Destroys the timer _(and stops it,if it was running)_
|
|
157
|
+
*
|
|
158
|
+
* @deprecated Timers take care of their own cleanup
|
|
159
|
+
*/
|
|
160
|
+
destroy(): void;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Pauses the timer _(if it was running)_
|
|
164
|
+
*/
|
|
165
|
+
pause(): When;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Start the timer
|
|
169
|
+
*
|
|
170
|
+
* @param resolve Optional resolve callback
|
|
171
|
+
* @returns Promise that resolves when the condition is met
|
|
172
|
+
*/
|
|
173
|
+
start(resolve?: (() => void) | null): Promise<void>;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Stops the timer _(if it was running)_
|
|
177
|
+
*/
|
|
178
|
+
stop(): When;
|
|
179
|
+
};
|
|
58
180
|
|
|
59
181
|
/**
|
|
60
182
|
* Options for a conditional timer
|
|
@@ -78,6 +200,7 @@ export type WhenState = {
|
|
|
78
200
|
promise: Promise<void>;
|
|
79
201
|
rejecter?: () => void;
|
|
80
202
|
resolver?: () => void;
|
|
203
|
+
result: boolean;
|
|
81
204
|
started: boolean;
|
|
82
205
|
timer: Timer;
|
|
83
206
|
};
|
|
@@ -91,7 +214,7 @@ export type WorkHandler = (
|
|
|
91
214
|
|
|
92
215
|
export type WorkHandlerTimer = {
|
|
93
216
|
instance: Timer;
|
|
94
|
-
|
|
217
|
+
name: TimerName;
|
|
95
218
|
};
|
|
96
219
|
|
|
97
220
|
export type WorkHandlerType = 'continue' | 'pause' | 'restart' | 'start' | 'stop';
|