@oscarpalmer/timer 0.31.0 → 0.33.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.
@@ -2,25 +2,28 @@
2
2
 
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
- function stepper(now) {
6
- if (values.length === 7) {
7
- exports.milliseconds = Math.floor(
8
- values.slice(2).reduce((first, second) => first + second) / 5 * 2
9
- ) / 2;
10
- last = void 0;
11
- values.length = 0;
12
- } else {
13
- last ??= now;
14
- const difference = now - last;
15
- if (difference > 0) {
16
- values.push(difference);
5
+ function calculate() {
6
+ return new Promise((resolve) => {
7
+ const values = [];
8
+ let last;
9
+ function step(now) {
10
+ if (last != null) {
11
+ values.push(now - last);
12
+ }
13
+ last = now;
14
+ if (values.length >= 10) {
15
+ const median = values.sort().slice(2, -2).reduce((first, second) => first + second, 0) / (values.length - 4);
16
+ resolve(median);
17
+ } else {
18
+ requestAnimationFrame(step);
19
+ }
17
20
  }
18
- last = now;
19
- requestAnimationFrame(stepper);
20
- }
21
+ requestAnimationFrame(step);
22
+ });
21
23
  }
22
24
  const activeTimers = /* @__PURE__ */ new Set();
23
25
  const beginTypes = /* @__PURE__ */ new Set(["continue", "start"]);
26
+ const intervalBuffer = 5;
24
27
  const destroyedMessage = "Timer has already been destroyed";
25
28
  const endTypes = /* @__PURE__ */ new Set(["pause", "stop"]);
26
29
  const endOrRestartTypes = /* @__PURE__ */ new Set([
@@ -31,10 +34,10 @@ const endOrRestartTypes = /* @__PURE__ */ new Set([
31
34
  const hiddenTimers = /* @__PURE__ */ new Set();
32
35
  const pauseTypes = /* @__PURE__ */ new Set(["continue", "pause"]);
33
36
  const startedMessage = "Timer has already been started";
34
- const values = [];
35
- let last;
36
- exports.milliseconds = Math.floor(1e3 / 60 * 2) / 2;
37
- requestAnimationFrame(stepper);
37
+ exports.milliseconds = 1e3 / 60;
38
+ calculate().then((value) => {
39
+ exports.milliseconds = value;
40
+ });
38
41
 
39
42
  exports.activeTimers = activeTimers;
40
43
  exports.beginTypes = beginTypes;
@@ -42,5 +45,6 @@ exports.destroyedMessage = destroyedMessage;
42
45
  exports.endOrRestartTypes = endOrRestartTypes;
43
46
  exports.endTypes = endTypes;
44
47
  exports.hiddenTimers = hiddenTimers;
48
+ exports.intervalBuffer = intervalBuffer;
45
49
  exports.pauseTypes = pauseTypes;
46
50
  exports.startedMessage = startedMessage;
package/dist/constants.js CHANGED
@@ -1,22 +1,25 @@
1
- function stepper(now) {
2
- if (values.length === 7) {
3
- milliseconds = Math.floor(
4
- values.slice(2).reduce((first, second) => first + second) / 5 * 2
5
- ) / 2;
6
- last = void 0;
7
- values.length = 0;
8
- } else {
9
- last ??= now;
10
- const difference = now - last;
11
- if (difference > 0) {
12
- 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.sort().slice(2, -2).reduce((first, second) => first + second, 0) / (values.length - 4);
12
+ resolve(median);
13
+ } else {
14
+ requestAnimationFrame(step);
15
+ }
13
16
  }
14
- last = now;
15
- requestAnimationFrame(stepper);
16
- }
17
+ requestAnimationFrame(step);
18
+ });
17
19
  }
18
20
  const activeTimers = /* @__PURE__ */ new Set();
19
21
  const beginTypes = /* @__PURE__ */ new Set(["continue", "start"]);
22
+ const intervalBuffer = 5;
20
23
  const destroyedMessage = "Timer has already been destroyed";
21
24
  const endTypes = /* @__PURE__ */ new Set(["pause", "stop"]);
22
25
  const endOrRestartTypes = /* @__PURE__ */ new Set([
@@ -27,9 +30,9 @@ const endOrRestartTypes = /* @__PURE__ */ new Set([
27
30
  const hiddenTimers = /* @__PURE__ */ new Set();
28
31
  const pauseTypes = /* @__PURE__ */ new Set(["continue", "pause"]);
29
32
  const startedMessage = "Timer has already been started";
30
- const values = [];
31
- let last;
32
- let milliseconds = Math.floor(1e3 / 60 * 2) / 2;
33
- requestAnimationFrame(stepper);
33
+ let milliseconds = 1e3 / 60;
34
+ calculate().then((value) => {
35
+ milliseconds = value;
36
+ });
34
37
 
35
- export { activeTimers, beginTypes, destroyedMessage, endOrRestartTypes, endTypes, hiddenTimers, milliseconds, pauseTypes, startedMessage };
38
+ export { activeTimers, beginTypes, destroyedMessage, endOrRestartTypes, endTypes, hiddenTimers, intervalBuffer, milliseconds, pauseTypes, startedMessage };
package/dist/delay.cjs CHANGED
@@ -11,7 +11,7 @@ function delay(time) {
11
11
  let start;
12
12
  function step(now) {
13
13
  start ??= now;
14
- if (interval === constants.milliseconds || now - start >= interval - 5) {
14
+ if (interval === constants.milliseconds || now - start >= interval - constants.intervalBuffer) {
15
15
  resolve();
16
16
  } else {
17
17
  requestAnimationFrame(step);
package/dist/delay.js CHANGED
@@ -1,4 +1,4 @@
1
- import { milliseconds } from './constants.js';
1
+ import { milliseconds, intervalBuffer } from './constants.js';
2
2
  import { getValidNumber } from './get.js';
3
3
 
4
4
  function delay(time) {
@@ -7,7 +7,7 @@ function delay(time) {
7
7
  let start;
8
8
  function step(now) {
9
9
  start ??= now;
10
- if (interval === milliseconds || now - start >= interval - 5) {
10
+ if (interval === milliseconds || now - start >= interval - intervalBuffer) {
11
11
  resolve();
12
12
  } else {
13
13
  requestAnimationFrame(step);
package/dist/repeat.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 repeat(callback, options) {
12
12
  return new timer.Timer(
13
13
  "repeat",
14
- work.work,
15
14
  {
16
15
  callback: get.getCallback(callback),
17
16
  trace: new models.TimerTrace().stack
package/dist/repeat.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 repeat(callback, options) {
8
8
  return new Timer(
9
9
  "repeat",
10
- work,
11
10
  {
12
11
  callback: getCallback(callback),
13
12
  trace: new TimerTrace().stack
package/dist/timer.cjs CHANGED
@@ -3,10 +3,10 @@
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
4
 
5
5
  const _function = require('@oscarpalmer/atoms/function');
6
+ const work = require('./work.cjs');
6
7
 
7
8
  class Timer {
8
- constructor(type, worker, state, options, start) {
9
- this.worker = worker;
9
+ constructor(type, state, options, start) {
10
10
  this.options = options;
11
11
  this.$timer = type;
12
12
  this.state = {
@@ -62,8 +62,17 @@ class Timer {
62
62
  this.options.onAfter = _function.noop;
63
63
  this.options.onError = _function.noop;
64
64
  this.state.callback = _function.noop;
65
- this.state.trace = void 0;
66
- this.#work("stop");
65
+ if (!globalThis._oscarpalmer_timer_debug) {
66
+ this.state.trace = void 0;
67
+ }
68
+ work.stop(
69
+ {
70
+ instance: this,
71
+ type: this.$timer
72
+ },
73
+ this.state,
74
+ this.options
75
+ );
67
76
  }
68
77
  /**
69
78
  * Pause the timer _(if it's running)_
@@ -90,7 +99,7 @@ class Timer {
90
99
  return this.#work("stop");
91
100
  }
92
101
  #work(type) {
93
- return this.worker(
102
+ return work.work(
94
103
  type,
95
104
  {
96
105
  instance: this,
@@ -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 last;
191
+ return function step(now) {
192
+ if (!state.active) {
193
+ return;
194
+ }
195
+ last ??= now;
196
+ const difference = now - last;
197
+ state.elapsed += difference;
198
+ state.total += difference;
199
+ last = now;
200
+ if (options.timeout > 0 && state.total >= options.timeout) {
201
+ options.onError?.();
202
+ finish(timer, state, options, false);
203
+ return;
204
+ }
205
+ if (options.interval === milliseconds ||
206
+ state.elapsed >= options.interval - intervalBuffer) {
207
+ if (options.count > -1) {
208
+ state.callback(state.index);
209
+ }
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 = {
@@ -210,8 +303,13 @@ class Timer {
210
303
  this.options.onAfter = noop;
211
304
  this.options.onError = noop;
212
305
  this.state.callback = noop;
213
- this.state.trace = undefined;
214
- this.#work('stop');
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);
215
313
  }
216
314
  /**
217
315
  * Pause the timer _(if it's running)_
@@ -238,89 +336,18 @@ class Timer {
238
336
  return this.#work('stop');
239
337
  }
240
338
  #work(type) {
241
- return this.worker(type, {
339
+ return work(type, {
242
340
  instance: this,
243
341
  type: this.$timer,
244
342
  }, this.state, this.options);
245
343
  }
246
344
  }
247
345
 
248
- function finish(timer, state, options, success) {
249
- activeTimers.delete(timer.instance);
250
- state.active = false;
251
- state.elapsed = 0;
252
- state.frame = undefined;
253
- if (timer.type === 'wait') {
254
- state.callback();
255
- }
256
- else {
257
- options.onAfter?.(success);
258
- }
259
- }
260
- function work(type, timer, state, options) {
261
- if ((state.destroyed && type !== 'stop') ||
262
- (state.active ? beginTypes.has(type) : endTypes.has(type))) {
263
- return timer.instance;
264
- }
265
- const pausable = pauseTypes.has(type);
266
- state.elapsed = pausable ? state.elapsed : 0;
267
- state.index = pausable ? state.index : 0;
268
- state.total = pausable ? state.total : 0;
269
- if (endOrRestartTypes.has(type)) {
270
- activeTimers.delete(timer.instance);
271
- cancelAnimationFrame(state.frame);
272
- if (type === 'stop') {
273
- options.onAfter?.(false);
274
- }
275
- state.active = false;
276
- state.frame = undefined;
277
- state.paused = type === 'pause';
278
- return type === 'restart'
279
- ? work('start', timer, state, options)
280
- : timer.instance;
281
- }
282
- state.active = true;
283
- state.paused = false;
284
- let start;
285
- function step(now) {
286
- if (!state.active) {
287
- return;
288
- }
289
- start ??= now;
290
- const difference = now - start;
291
- state.elapsed += difference;
292
- state.total += difference;
293
- if (options.timeout > 0 && state.total >= options.timeout) {
294
- options.onError?.();
295
- finish(timer, state, options, false);
296
- return;
297
- }
298
- if (options.interval === milliseconds ||
299
- state.elapsed >= options.interval - 5) {
300
- if (options.count > -1) {
301
- state.callback(state.index);
302
- }
303
- start = now;
304
- state.elapsed = 0;
305
- state.index += 1;
306
- if (options.count === -1 ||
307
- (options.count > 0 && state.index >= options.count)) {
308
- finish(timer, state, options, true);
309
- return;
310
- }
311
- }
312
- state.frame = requestAnimationFrame(step);
313
- }
314
- activeTimers.add(timer.instance);
315
- state.frame = requestAnimationFrame(step);
316
- return timer.instance;
317
- }
318
-
319
346
  /**
320
347
  * Create a repeating timer
321
348
  */
322
349
  function repeat(callback, options) {
323
- return new Timer('repeat', work, {
350
+ return new Timer('repeat', {
324
351
  callback: getCallback(callback),
325
352
  trace: new TimerTrace().stack,
326
353
  }, {
@@ -338,7 +365,7 @@ function repeat(callback, options) {
338
365
  * @param time How long to wait for _(in milliseconds; defaults to screen refresh rate)_
339
366
  */
340
367
  function wait(callback, time) {
341
- return new Timer('wait', work, {
368
+ return new Timer('wait', {
342
369
  callback: getCallback(callback),
343
370
  trace: new TimerTrace().stack,
344
371
  }, {
@@ -352,7 +379,13 @@ function wait(callback, time) {
352
379
 
353
380
  class When {
354
381
  $timer = 'when';
355
- state;
382
+ state = {
383
+ promise: undefined,
384
+ rejecter: undefined,
385
+ resolver: undefined,
386
+ started: false,
387
+ timer: undefined,
388
+ };
356
389
  /**
357
390
  * Is the timer active?
358
391
  */
@@ -379,8 +412,44 @@ class When {
379
412
  ? this.state.timer?.trace
380
413
  : undefined;
381
414
  }
382
- constructor(state) {
383
- 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);
384
453
  }
385
454
  /**
386
455
  * Continues the timer _(if it was paused)_
@@ -393,11 +462,12 @@ class When {
393
462
  * Destroys the timer _(and stops it,if it was running)_
394
463
  */
395
464
  destroy() {
396
- this.state.timer?.destroy();
397
- this.state.promise = undefined;
398
- this.state.resolver = noop;
399
- this.state.rejecter = noop;
400
- 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;
401
471
  }
402
472
  /**
403
473
  * Pauses the timer _(if it was running)_
@@ -418,59 +488,23 @@ class When {
418
488
  */
419
489
  // biome-ignore lint/suspicious/noThenProperty: returning a promise-like object, so it's ok ;)
420
490
  then(resolve, reject) {
421
- if (this.state.timer == null || this.state?.started) {
422
- throw new Error(this.state.timer == null ? destroyedMessage : startedMessage);
491
+ const { state } = this;
492
+ if (state.timer == null) {
493
+ throw new Error(destroyedMessage);
423
494
  }
424
- this.state.started = true;
425
- this.state.timer.start();
426
- 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);
427
501
  }
428
502
  }
429
503
  /**
430
504
  * Create a conditional timer
431
505
  */
432
506
  function when(condition, options) {
433
- let called = false;
434
- let result = false;
435
- const state = {
436
- started: false,
437
- timer: new Timer('when', work, {
438
- callback() {
439
- if (condition()) {
440
- result = true;
441
- state.timer.stop();
442
- }
443
- },
444
- trace: new TimerTrace().stack,
445
- }, {
446
- onAfter() {
447
- if (!(state.timer?.paused ?? false) && !called) {
448
- called = true;
449
- if (result) {
450
- state.resolver?.();
451
- }
452
- else {
453
- state.rejecter?.();
454
- }
455
- instance.destroy();
456
- }
457
- },
458
- onError() {
459
- state.rejecter?.();
460
- instance.destroy();
461
- },
462
- count: getValidNumber(options?.count),
463
- interval: getValidNumber(options?.interval, milliseconds),
464
- timeout: getValidNumber(options?.timeout),
465
- }, false),
466
- };
467
- const promise = new Promise((resolve, reject) => {
468
- state.resolver = resolve;
469
- state.rejecter = reject;
470
- });
471
- state.promise = promise;
472
- const instance = new When(state);
473
- return instance;
507
+ return new When(condition, options);
474
508
  }
475
509
 
476
510
  export { delay, isRepeated, isTimer, isWaited, isWhen, repeat, wait, when };