@oscarpalmer/timer 0.30.0 → 0.32.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.
@@ -1,21 +1,26 @@
1
- /**
2
- * Stepper to calculate the average refresh rate of the display
3
- */
4
- function stepper(now) {
5
- if (values.length === 7) {
6
- milliseconds = Math.floor((values.slice(2).reduce((first, second) => first + second) / 5) * 2) / 2;
7
- last = undefined;
8
- values.length = 0;
9
- }
10
- else {
11
- last ??= now;
12
- const difference = now - last;
13
- if (difference > 0) {
14
- values.push(difference);
1
+ function calculate() {
2
+ return new Promise(resolve => {
3
+ const values = [];
4
+ let last;
5
+ function step(now) {
6
+ if (last != null) {
7
+ values.push(now - last);
8
+ }
9
+ last = now;
10
+ if (values.length >= 10) {
11
+ const median = values
12
+ .sort()
13
+ .slice(2, -2)
14
+ .reduce((first, second) => first + second, 0) /
15
+ (values.length - 4);
16
+ resolve(median);
17
+ }
18
+ else {
19
+ requestAnimationFrame(step);
20
+ }
15
21
  }
16
- last = now;
17
- requestAnimationFrame(stepper);
18
- }
22
+ requestAnimationFrame(step);
23
+ });
19
24
  }
20
25
  /**
21
26
  * A set of all active timers
@@ -25,6 +30,10 @@ const activeTimers = new Set();
25
30
  * A set of types that allow work to begin
26
31
  */
27
32
  const beginTypes = new Set(['continue', 'start']);
33
+ /**
34
+ * Buffer value to use when evaluating if a specific time is within a certain range
35
+ */
36
+ const intervalBuffer = 5;
28
37
  /**
29
38
  * Message to show when a when-timer is destroyed
30
39
  */
@@ -51,13 +60,13 @@ const pauseTypes = new Set(['continue', 'pause']);
51
60
  */
52
61
  const startedMessage = 'Timer has already been started';
53
62
  //
54
- const values = [];
55
- let last;
56
63
  /**
57
64
  * A calculated average of the refresh rate of the display _(in milliseconds)_
58
65
  */
59
- let milliseconds = Math.floor((1000 / 60) * 2) / 2;
60
- requestAnimationFrame(stepper);
66
+ let milliseconds = 1000 / 60;
67
+ calculate().then(value => {
68
+ milliseconds = value;
69
+ });
61
70
 
62
71
  if (globalThis._oscarpalmer_timers == null) {
63
72
  Object.defineProperty(globalThis, '_oscarpalmer_timers', {
@@ -102,7 +111,8 @@ function delay(time) {
102
111
  let start;
103
112
  function step(now) {
104
113
  start ??= now;
105
- if (interval === milliseconds || now - start >= interval - 5) {
114
+ if (interval === milliseconds ||
115
+ now - start >= interval - intervalBuffer) {
106
116
  resolve();
107
117
  }
108
118
  else {
@@ -148,8 +158,92 @@ class TimerTrace extends Error {
148
158
  }
149
159
  }
150
160
 
161
+ function endOrRestart(type, timer, state, options) {
162
+ cancelAnimationFrame(state.frame);
163
+ if (type === 'stop') {
164
+ options.onAfter?.(false);
165
+ }
166
+ state.active = false;
167
+ state.frame = undefined;
168
+ state.paused = type === 'pause';
169
+ return type === 'restart'
170
+ ? work('start', timer, state, options)
171
+ : timer.instance;
172
+ }
173
+ function finish(timer, state, options, success) {
174
+ activeTimers.delete(timer.instance);
175
+ state.active = false;
176
+ state.elapsed = 0;
177
+ state.frame = undefined;
178
+ if (timer.type === 'wait') {
179
+ state.callback();
180
+ }
181
+ else {
182
+ options.onAfter?.(success);
183
+ }
184
+ }
185
+ function ignore(type, state) {
186
+ return ((state.destroyed && type !== 'stop') ||
187
+ (state.active ? beginTypes.has(type) : endTypes.has(type)));
188
+ }
189
+ function run(timer, state, options) {
190
+ let start;
191
+ return function step(now) {
192
+ if (!state.active) {
193
+ return;
194
+ }
195
+ start ??= now;
196
+ const difference = now - start;
197
+ state.elapsed += difference;
198
+ state.total += difference;
199
+ if (options.timeout > 0 && state.total >= options.timeout) {
200
+ options.onError?.();
201
+ finish(timer, state, options, false);
202
+ return;
203
+ }
204
+ if (options.interval === milliseconds ||
205
+ state.elapsed >= options.interval - intervalBuffer) {
206
+ if (options.count > -1) {
207
+ state.callback(state.index);
208
+ }
209
+ start = now;
210
+ state.elapsed = 0;
211
+ state.index += 1;
212
+ if (options.count === -1 ||
213
+ (options.count > 0 && state.index >= options.count)) {
214
+ finish(timer, state, options, true);
215
+ return;
216
+ }
217
+ }
218
+ state.frame = requestAnimationFrame(step);
219
+ };
220
+ }
221
+ function setState(type, state) {
222
+ const pausable = pauseTypes.has(type);
223
+ state.elapsed = pausable ? state.elapsed : 0;
224
+ state.index = pausable ? state.index : 0;
225
+ state.total = pausable ? state.total : 0;
226
+ }
227
+ function stop(timer, state, options) {
228
+ endOrRestart('stop', timer, state, options);
229
+ }
230
+ function work(type, timer, state, options) {
231
+ if (ignore(type, state)) {
232
+ return timer.instance;
233
+ }
234
+ setState(type, state);
235
+ if (endOrRestartTypes.has(type)) {
236
+ return endOrRestart(type, timer, state, options);
237
+ }
238
+ state.active = true;
239
+ state.paused = false;
240
+ activeTimers.add(timer.instance);
241
+ const runner = run(timer, state, options);
242
+ state.frame = requestAnimationFrame(runner);
243
+ return timer.instance;
244
+ }
245
+
151
246
  class Timer {
152
- worker;
153
247
  options;
154
248
  state;
155
249
  /**
@@ -178,8 +272,7 @@ class Timer {
178
272
  ? this.state.trace
179
273
  : undefined;
180
274
  }
181
- constructor(type, worker, state, options, start) {
182
- this.worker = worker;
275
+ constructor(type, state, options, start) {
183
276
  this.options = options;
184
277
  this.$timer = type;
185
278
  this.state = {
@@ -207,7 +300,16 @@ class Timer {
207
300
  */
208
301
  destroy() {
209
302
  this.state.destroyed = true;
210
- this.#work('stop');
303
+ this.options.onAfter = noop;
304
+ this.options.onError = noop;
305
+ this.state.callback = noop;
306
+ if (!globalThis._oscarpalmer_timer_debug) {
307
+ this.state.trace = undefined;
308
+ }
309
+ stop({
310
+ instance: this,
311
+ type: this.$timer,
312
+ }, this.state, this.options);
211
313
  }
212
314
  /**
213
315
  * Pause the timer _(if it's running)_
@@ -234,89 +336,18 @@ class Timer {
234
336
  return this.#work('stop');
235
337
  }
236
338
  #work(type) {
237
- return this.worker(type, {
339
+ return work(type, {
238
340
  instance: this,
239
341
  type: this.$timer,
240
342
  }, this.state, this.options);
241
343
  }
242
344
  }
243
345
 
244
- function finish(timer, state, options, success) {
245
- activeTimers.delete(timer.instance);
246
- state.active = false;
247
- state.elapsed = 0;
248
- state.frame = undefined;
249
- if (timer.type === 'wait') {
250
- state.callback();
251
- }
252
- else {
253
- options.onAfter?.(success);
254
- }
255
- }
256
- function work(type, timer, state, options) {
257
- if ((state.destroyed && type !== 'stop') ||
258
- (state.active ? beginTypes.has(type) : endTypes.has(type))) {
259
- return timer.instance;
260
- }
261
- const pausable = pauseTypes.has(type);
262
- state.elapsed = pausable ? state.elapsed : 0;
263
- state.index = pausable ? state.index : 0;
264
- state.total = pausable ? state.total : 0;
265
- if (endOrRestartTypes.has(type)) {
266
- activeTimers.delete(timer.instance);
267
- cancelAnimationFrame(state.frame);
268
- if (type === 'stop') {
269
- options.onAfter?.(false);
270
- }
271
- state.active = false;
272
- state.frame = undefined;
273
- state.paused = type === 'pause';
274
- return type === 'restart'
275
- ? work('start', timer, state, options)
276
- : timer.instance;
277
- }
278
- state.active = true;
279
- state.paused = false;
280
- let start;
281
- function step(now) {
282
- if (!state.active) {
283
- return;
284
- }
285
- start ??= now;
286
- const difference = now - start;
287
- state.elapsed += difference;
288
- state.total += difference;
289
- if (options.timeout > 0 && state.total >= options.timeout) {
290
- options.onError?.();
291
- finish(timer, state, options, false);
292
- return;
293
- }
294
- if (options.interval === milliseconds ||
295
- state.elapsed >= options.interval - 5) {
296
- if (options.count > -1) {
297
- state.callback(state.index);
298
- }
299
- start = now;
300
- state.elapsed = 0;
301
- state.index += 1;
302
- if (options.count === -1 ||
303
- (options.count > 0 && state.index >= options.count)) {
304
- finish(timer, state, options, true);
305
- return;
306
- }
307
- }
308
- state.frame = requestAnimationFrame(step);
309
- }
310
- activeTimers.add(timer.instance);
311
- state.frame = requestAnimationFrame(step);
312
- return timer.instance;
313
- }
314
-
315
346
  /**
316
347
  * Create a repeating timer
317
348
  */
318
349
  function repeat(callback, options) {
319
- return new Timer('repeat', work, {
350
+ return new Timer('repeat', {
320
351
  callback: getCallback(callback),
321
352
  trace: new TimerTrace().stack,
322
353
  }, {
@@ -334,7 +365,7 @@ function repeat(callback, options) {
334
365
  * @param time How long to wait for _(in milliseconds; defaults to screen refresh rate)_
335
366
  */
336
367
  function wait(callback, time) {
337
- return new Timer('wait', work, {
368
+ return new Timer('wait', {
338
369
  callback: getCallback(callback),
339
370
  trace: new TimerTrace().stack,
340
371
  }, {
@@ -348,7 +379,13 @@ function wait(callback, time) {
348
379
 
349
380
  class When {
350
381
  $timer = 'when';
351
- state;
382
+ state = {
383
+ promise: undefined,
384
+ rejecter: undefined,
385
+ resolver: undefined,
386
+ started: false,
387
+ timer: undefined,
388
+ };
352
389
  /**
353
390
  * Is the timer active?
354
391
  */
@@ -375,8 +412,44 @@ class When {
375
412
  ? this.state.timer?.trace
376
413
  : undefined;
377
414
  }
378
- constructor(state) {
379
- this.state = state;
415
+ constructor(condition, options) {
416
+ const { state } = this;
417
+ state.promise = new Promise((resolve, reject) => {
418
+ state.resolver = resolve;
419
+ state.rejecter = reject;
420
+ });
421
+ let result = false;
422
+ this.state.timer = new Timer('when', {
423
+ callback() {
424
+ try {
425
+ if (condition()) {
426
+ result = true;
427
+ state.timer.stop();
428
+ }
429
+ }
430
+ catch {
431
+ state.timer.stop();
432
+ }
433
+ },
434
+ trace: new TimerTrace().stack,
435
+ }, {
436
+ onAfter: () => {
437
+ if (result) {
438
+ state.resolver?.();
439
+ }
440
+ else {
441
+ state.rejecter?.();
442
+ }
443
+ this.destroy();
444
+ },
445
+ onError: () => {
446
+ state.rejecter?.();
447
+ this.destroy();
448
+ },
449
+ count: getValidNumber(options?.count),
450
+ interval: getValidNumber(options?.interval, milliseconds),
451
+ timeout: getValidNumber(options?.timeout),
452
+ }, false);
380
453
  }
381
454
  /**
382
455
  * Continues the timer _(if it was paused)_
@@ -389,11 +462,12 @@ class When {
389
462
  * Destroys the timer _(and stops it,if it was running)_
390
463
  */
391
464
  destroy() {
392
- this.state.timer?.destroy();
393
- this.state.promise = undefined;
394
- this.state.resolver = noop;
395
- this.state.rejecter = noop;
396
- this.state.timer = undefined;
465
+ const { state } = this;
466
+ state.timer?.destroy();
467
+ state.promise = undefined;
468
+ state.resolver = noop;
469
+ state.rejecter = noop;
470
+ state.timer = undefined;
397
471
  }
398
472
  /**
399
473
  * Pauses the timer _(if it was running)_
@@ -414,59 +488,23 @@ class When {
414
488
  */
415
489
  // biome-ignore lint/suspicious/noThenProperty: returning a promise-like object, so it's ok ;)
416
490
  then(resolve, reject) {
417
- if (this.state.timer == null || this.state?.started) {
418
- throw new Error(this.state.timer == null ? destroyedMessage : startedMessage);
491
+ const { state } = this;
492
+ if (state.timer == null) {
493
+ throw new Error(destroyedMessage);
419
494
  }
420
- this.state.started = true;
421
- this.state.timer.start();
422
- return this.state.promise.then(resolve ?? noop, reject ?? noop);
495
+ if (state.started) {
496
+ throw new Error(startedMessage);
497
+ }
498
+ state.started = true;
499
+ state.timer.start();
500
+ return state.promise.then(resolve ?? noop, reject ?? noop);
423
501
  }
424
502
  }
425
503
  /**
426
504
  * Create a conditional timer
427
505
  */
428
506
  function when(condition, options) {
429
- let called = false;
430
- let result = false;
431
- const state = {
432
- started: false,
433
- timer: new Timer('when', work, {
434
- callback() {
435
- if (condition()) {
436
- result = true;
437
- state.timer.stop();
438
- }
439
- },
440
- trace: new TimerTrace().stack,
441
- }, {
442
- onAfter() {
443
- if (!(state.timer?.paused ?? false) && !called) {
444
- called = true;
445
- if (result) {
446
- state.resolver?.();
447
- }
448
- else {
449
- state.rejecter?.();
450
- }
451
- instance.destroy();
452
- }
453
- },
454
- onError() {
455
- state.rejecter?.();
456
- instance.destroy();
457
- },
458
- count: getValidNumber(options?.count),
459
- interval: getValidNumber(options?.interval, milliseconds),
460
- timeout: getValidNumber(options?.timeout),
461
- }, false),
462
- };
463
- const promise = new Promise((resolve, reject) => {
464
- state.resolver = resolve;
465
- state.rejecter = reject;
466
- });
467
- state.promise = promise;
468
- const instance = new When(state);
469
- return instance;
507
+ return new When(condition, options);
470
508
  }
471
509
 
472
510
  export { delay, isRepeated, isTimer, isWaited, isWhen, repeat, wait, when };
package/dist/timer.js CHANGED
@@ -1,6 +1,8 @@
1
+ import { noop } from '@oscarpalmer/atoms/function';
2
+ import { stop, work } from './work.js';
3
+
1
4
  class Timer {
2
- constructor(type, worker, state, options, start) {
3
- this.worker = worker;
5
+ constructor(type, state, options, start) {
4
6
  this.options = options;
5
7
  this.$timer = type;
6
8
  this.state = {
@@ -53,7 +55,20 @@ class Timer {
53
55
  */
54
56
  destroy() {
55
57
  this.state.destroyed = true;
56
- this.#work("stop");
58
+ this.options.onAfter = noop;
59
+ this.options.onError = noop;
60
+ this.state.callback = noop;
61
+ if (!globalThis._oscarpalmer_timer_debug) {
62
+ this.state.trace = void 0;
63
+ }
64
+ stop(
65
+ {
66
+ instance: this,
67
+ type: this.$timer
68
+ },
69
+ this.state,
70
+ this.options
71
+ );
57
72
  }
58
73
  /**
59
74
  * Pause the timer _(if it's running)_
@@ -80,7 +95,7 @@ class Timer {
80
95
  return this.#work("stop");
81
96
  }
82
97
  #work(type) {
83
- return this.worker(
98
+ return work(
84
99
  type,
85
100
  {
86
101
  instance: this,
package/dist/wait.cjs CHANGED
@@ -4,14 +4,13 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const constants = require('./constants.cjs');
6
6
  const get = require('./get.cjs');
7
+ require('./global.cjs');
7
8
  const models = require('./models.cjs');
8
9
  const timer = require('./timer.cjs');
9
- const work = require('./work.cjs');
10
10
 
11
11
  function wait(callback, time) {
12
12
  return new timer.Timer(
13
13
  "wait",
14
- work.work,
15
14
  {
16
15
  callback: get.getCallback(callback),
17
16
  trace: new models.TimerTrace().stack
package/dist/wait.js CHANGED
@@ -1,13 +1,12 @@
1
1
  import { milliseconds } from './constants.js';
2
2
  import { getCallback, getValidNumber } from './get.js';
3
+ import './global.js';
3
4
  import { TimerTrace } from './models.js';
4
5
  import { Timer } from './timer.js';
5
- import { work } from './work.js';
6
6
 
7
7
  function wait(callback, time) {
8
8
  return new Timer(
9
9
  "wait",
10
- work,
11
10
  {
12
11
  callback: getCallback(callback),
13
12
  trace: new TimerTrace().stack
package/dist/when.cjs CHANGED
@@ -2,16 +2,22 @@
2
2
 
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
- const _function = require('./node_modules/@oscarpalmer/atoms/dist/function.cjs');
5
+ const _function = require('@oscarpalmer/atoms/function');
6
6
  const constants = require('./constants.cjs');
7
7
  const get = require('./get.cjs');
8
+ require('./global.cjs');
8
9
  const models = require('./models.cjs');
9
10
  const timer = require('./timer.cjs');
10
- const work = require('./work.cjs');
11
11
 
12
12
  class When {
13
13
  $timer = "when";
14
- state;
14
+ state = {
15
+ promise: void 0,
16
+ rejecter: void 0,
17
+ resolver: void 0,
18
+ started: false,
19
+ timer: void 0
20
+ };
15
21
  /**
16
22
  * Is the timer active?
17
23
  */
@@ -36,8 +42,47 @@ class When {
36
42
  get trace() {
37
43
  return globalThis._oscarpalmer_timer_debug ?? false ? this.state.timer?.trace : void 0;
38
44
  }
39
- constructor(state) {
40
- this.state = state;
45
+ constructor(condition, options) {
46
+ const { state } = this;
47
+ state.promise = new Promise((resolve, reject) => {
48
+ state.resolver = resolve;
49
+ state.rejecter = reject;
50
+ });
51
+ let result = false;
52
+ this.state.timer = new timer.Timer(
53
+ "when",
54
+ {
55
+ callback() {
56
+ try {
57
+ if (condition()) {
58
+ result = true;
59
+ state.timer.stop();
60
+ }
61
+ } catch {
62
+ state.timer.stop();
63
+ }
64
+ },
65
+ trace: new models.TimerTrace().stack
66
+ },
67
+ {
68
+ onAfter: () => {
69
+ if (result) {
70
+ state.resolver?.();
71
+ } else {
72
+ state.rejecter?.();
73
+ }
74
+ this.destroy();
75
+ },
76
+ onError: () => {
77
+ state.rejecter?.();
78
+ this.destroy();
79
+ },
80
+ count: get.getValidNumber(options?.count),
81
+ interval: get.getValidNumber(options?.interval, constants.milliseconds),
82
+ timeout: get.getValidNumber(options?.timeout)
83
+ },
84
+ false
85
+ );
41
86
  }
42
87
  /**
43
88
  * Continues the timer _(if it was paused)_
@@ -50,11 +95,12 @@ class When {
50
95
  * Destroys the timer _(and stops it,if it was running)_
51
96
  */
52
97
  destroy() {
53
- this.state.timer?.destroy();
54
- this.state.promise = void 0;
55
- this.state.resolver = _function.noop;
56
- this.state.rejecter = _function.noop;
57
- this.state.timer = void 0;
98
+ const { state } = this;
99
+ state.timer?.destroy();
100
+ state.promise = void 0;
101
+ state.resolver = _function.noop;
102
+ state.rejecter = _function.noop;
103
+ state.timer = void 0;
58
104
  }
59
105
  /**
60
106
  * Pauses the timer _(if it was running)_
@@ -75,63 +121,20 @@ class When {
75
121
  */
76
122
  // biome-ignore lint/suspicious/noThenProperty: returning a promise-like object, so it's ok ;)
77
123
  then(resolve, reject) {
78
- if (this.state.timer == null || this.state?.started) {
79
- throw new Error(
80
- this.state.timer == null ? constants.destroyedMessage : constants.startedMessage
81
- );
124
+ const { state } = this;
125
+ if (state.timer == null) {
126
+ throw new Error(constants.destroyedMessage);
127
+ }
128
+ if (state.started) {
129
+ throw new Error(constants.startedMessage);
82
130
  }
83
- this.state.started = true;
84
- this.state.timer.start();
85
- return this.state.promise.then(resolve ?? _function.noop, reject ?? _function.noop);
131
+ state.started = true;
132
+ state.timer.start();
133
+ return state.promise.then(resolve ?? _function.noop, reject ?? _function.noop);
86
134
  }
87
135
  }
88
136
  function when(condition, options) {
89
- let called = false;
90
- let result = false;
91
- const state = {
92
- started: false,
93
- timer: new timer.Timer(
94
- "when",
95
- work.work,
96
- {
97
- callback() {
98
- if (condition()) {
99
- result = true;
100
- state.timer.stop();
101
- }
102
- },
103
- trace: new models.TimerTrace().stack
104
- },
105
- {
106
- onAfter() {
107
- if (!(state.timer?.paused ?? false) && !called) {
108
- called = true;
109
- if (result) {
110
- state.resolver?.();
111
- } else {
112
- state.rejecter?.();
113
- }
114
- instance.destroy();
115
- }
116
- },
117
- onError() {
118
- state.rejecter?.();
119
- instance.destroy();
120
- },
121
- count: get.getValidNumber(options?.count),
122
- interval: get.getValidNumber(options?.interval, constants.milliseconds),
123
- timeout: get.getValidNumber(options?.timeout)
124
- },
125
- false
126
- )
127
- };
128
- const promise = new Promise((resolve, reject) => {
129
- state.resolver = resolve;
130
- state.rejecter = reject;
131
- });
132
- state.promise = promise;
133
- const instance = new When(state);
134
- return instance;
137
+ return new When(condition, options);
135
138
  }
136
139
 
137
140
  exports.when = when;