@oscarpalmer/atoms 0.150.0 → 0.152.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,27 +1,3 @@
1
- function calculate() {
2
- return new Promise((resolve) => {
3
- const values = [];
4
- let last;
5
- function step(now) {
6
- if (last != null) values.push(now - last);
7
- last = now;
8
- if (values.length >= TOTAL) resolve(values.sort().slice(TRIM_PART, -TRIM_PART).reduce((first, second) => first + second, 0) / (values.length - TRIM_TOTAL));
9
- else requestAnimationFrame(step);
10
- }
11
- requestAnimationFrame(step);
12
- });
13
- }
14
- const TOTAL = 10;
15
- const TRIM_PART = 2;
16
- const TRIM_TOTAL = 4;
17
- let FRAME_RATE_MS = 1e3 / 60;
18
- /**
19
- * A calculated average of the refresh rate of the display _(in milliseconds)_
20
- */
21
- calculate().then((value) => {
22
- FRAME_RATE_MS = value;
23
- });
24
- var frame_rate_default = FRAME_RATE_MS;
25
1
  function getArrayCallback(value) {
26
2
  switch (typeof value) {
27
3
  case "function": return value;
@@ -701,44 +677,40 @@ function unique(array, key) {
701
677
  if (!Array.isArray(array)) return [];
702
678
  return array.length > 1 ? findValues("unique", array, [key, void 0]).matched : array;
703
679
  }
704
- function getLimiter(callback, throttler, time) {
705
- const interval = typeof time === "number" && time >= frame_rate_default ? time : frame_rate_default;
706
- function step(now, parameters) {
707
- if (interval === frame_rate_default || now - timestamp >= interval) {
708
- if (throttler) timestamp = now;
709
- callback(...parameters);
710
- } else frame = requestAnimationFrame((next) => {
711
- step(next, parameters);
712
- });
680
+ function getInterval(value) {
681
+ return typeof value === "number" && value > 0 ? value : 0;
682
+ }
683
+ function getTimer(type, callback, time) {
684
+ const interval = getInterval(time);
685
+ function run(now) {
686
+ start ??= now;
687
+ if (interval === 0 || now - start >= interval - OFFSET) {
688
+ if (throttle) start = now;
689
+ callback(...args);
690
+ } else frame = requestAnimationFrame(run);
713
691
  }
692
+ const throttle = type === TIMER_THROTTLE;
693
+ let args;
714
694
  let frame;
715
- let timestamp;
716
- const limiter = (...parameters) => {
717
- limiter.cancel();
718
- frame = requestAnimationFrame((now) => {
719
- timestamp ??= now - frame_rate_default;
720
- step(now, parameters);
721
- });
695
+ let start;
696
+ const timer = (...parameters) => {
697
+ timer.cancel();
698
+ args = parameters;
699
+ frame = requestAnimationFrame(run);
722
700
  };
723
- limiter.cancel = () => {
724
- if (frame != null) {
725
- cancelAnimationFrame(frame);
726
- frame = void 0;
727
- }
701
+ timer.cancel = () => {
702
+ cancelAnimationFrame(frame);
728
703
  };
729
- return limiter;
704
+ return timer;
730
705
  }
706
+ const OFFSET = 5;
707
+ const TIMER_DEBOUNCE = "debounce";
708
+ const TIMER_THROTTLE = "throttle";
709
+ const TIMER_WAIT = "wait";
731
710
  /**
732
- * Debounce a function, ensuring it is only called after `time` milliseconds have passed
733
- *
734
- * On subsequent calls, the timer is reset and will wait another `time` milliseconds _(and so on...)_
735
- * @param callback Callback to debounce
736
- * @param time Time in milliseconds to wait before calling the callback _(defaults to match frame rate)_
737
- * @returns Debounced callback with a `cancel` method
711
+ * A function that does nothing, which can be useful, I guess…
738
712
  */
739
- function debounce(callback, time) {
740
- return getLimiter(callback, false, time);
741
- }
713
+ function noop() {}
742
714
  /**
743
715
  * Is the number between a minimum and maximum value?
744
716
  * @param value Value to check
@@ -946,13 +918,24 @@ function memoize(callback, options) {
946
918
  }
947
919
  const DEFAULT_CACHE_SIZE = 1024;
948
920
  /**
921
+ * Debounce a function, ensuring it is only called after `time` milliseconds have passed
922
+ *
923
+ * On subsequent calls, the timer is reset and will wait another `time` milliseconds _(and so on...)_
924
+ * @param callback Callback to debounce
925
+ * @param time Time in milliseconds to wait before calling the callback _(defaults to match frame rate)_
926
+ * @returns Debounced callback with a `cancel` method
927
+ */
928
+ function debounce(callback, time) {
929
+ return getTimer(TIMER_DEBOUNCE, callback, time);
930
+ }
931
+ /**
949
932
  * Throttle a function, ensuring it is only called once every `time` milliseconds
950
933
  * @param callback Callback to throttle
951
934
  * @param time Time in milliseconds to wait before calling the callback again _(defaults to match frame rate)_
952
935
  * @returns Throttled callback with a `cancel` method
953
936
  */
954
937
  function throttle(callback, time) {
955
- return getLimiter(callback, true, time);
938
+ return getTimer(TIMER_THROTTLE, callback, time);
956
939
  }
957
940
  function _isResult(value, okValue) {
958
941
  if (!isPlainObject(value)) return false;
@@ -1053,10 +1036,6 @@ const MESSAGE_PIPE_ARRAY = "Pipe expected to receive an array of functions";
1053
1036
  const MESSAGE_PIPE_PROMISE = "Synchronous Pipe received a promise. Use `pipe.async` instead.";
1054
1037
  const assertFlowFunctions = assert.condition((value) => Array.isArray(value) && value.every((item) => typeof item === "function"), MESSAGE_FLOW_ARRAY, TypeError);
1055
1038
  const assertPipeFunctions = assert.condition((value) => Array.isArray(value) && value.every((item) => typeof item === "function"), MESSAGE_PIPE_ARRAY, TypeError);
1056
- /**
1057
- * A function that does nothing, which can be useful, I guess…
1058
- */
1059
- function noop() {}
1060
1039
  function equal(first, second, options) {
1061
1040
  return equalValue(first, second, getEqualOptions(options));
1062
1041
  }
@@ -3147,23 +3126,19 @@ async function toResult(value) {
3147
3126
  }
3148
3127
  async function getTimedPromise(promise, time, signal) {
3149
3128
  function abort() {
3150
- cancelAnimationFrame(frame);
3129
+ timer.cancel();
3151
3130
  rejector(signal.reason);
3152
3131
  }
3153
- function run(now) {
3154
- start ??= now;
3155
- if (time === 0 || now - start >= time - 5) settlePromise(abort, rejector, new PromiseTimeoutError(), signal);
3156
- else frame = requestAnimationFrame(run);
3157
- }
3158
3132
  signal?.addEventListener(PROMISE_EVENT_NAME, abort, PROMISE_ABORT_OPTIONS);
3159
- let frame;
3133
+ const timer = getTimer(TIMER_WAIT, () => {
3134
+ settlePromise(abort, rejector, new PromiseTimeoutError(), signal);
3135
+ }, time);
3160
3136
  let rejector;
3161
- let start;
3162
3137
  return Promise.race([promise, new Promise((_, reject) => {
3163
3138
  rejector = reject;
3164
- frame = requestAnimationFrame(run);
3139
+ timer();
3165
3140
  })]).then((value) => {
3166
- cancelAnimationFrame(frame);
3141
+ timer.cancel();
3167
3142
  signal?.removeEventListener(PROMISE_EVENT_NAME, abort);
3168
3143
  return value;
3169
3144
  });
@@ -3178,24 +3153,20 @@ function delay(options) {
3178
3153
  const { signal, time } = getPromiseOptions(options);
3179
3154
  if (signal?.aborted ?? false) return Promise.reject(signal.reason);
3180
3155
  function abort() {
3181
- cancelAnimationFrame(frame);
3156
+ timer.cancel();
3182
3157
  rejector(signal.reason);
3183
3158
  }
3184
- function run(now) {
3185
- start ??= now;
3186
- if (now - start >= time - 5) settlePromise(abort, resolver, void 0, signal);
3187
- else frame = requestAnimationFrame(run);
3188
- }
3159
+ const timer = getTimer(TIMER_WAIT, () => {
3160
+ settlePromise(abort, resolver, void 0, signal);
3161
+ }, time);
3189
3162
  signal?.addEventListener("abort", abort, PROMISE_ABORT_OPTIONS);
3190
- let frame;
3191
3163
  let rejector;
3192
3164
  let resolver;
3193
- let start;
3194
3165
  return new Promise((resolve, reject) => {
3195
3166
  rejector = reject;
3196
3167
  resolver = resolve;
3197
3168
  if (time === 0) settlePromise(abort, resolve, void 0, signal);
3198
- else frame = requestAnimationFrame(run);
3169
+ else timer();
3199
3170
  });
3200
3171
  }
3201
3172
  async function attemptPromise(value, options) {
@@ -3725,4 +3696,4 @@ var SizedSet = class extends Set {
3725
3696
  }
3726
3697
  }
3727
3698
  };
3728
- export { CancelablePromise, frame_rate_default as FRAME_RATE_MS, PromiseTimeoutError, QueueError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, smush, snakeCase, sort, splice, startsWith, sum, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, unique, unsmush, unwrap, update, upperCase, words };
3699
+ export { CancelablePromise, PromiseTimeoutError, QueueError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, toResult, fromQuery, toPromise as fromResult, toPromise, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, smush, snakeCase, sort, splice, startsWith, sum, template, throttle, timed, times, titleCase, toMap, toQuery, toRecord, toSet, toggle, trim, truncate, tryDecode, tryEncode, unique, unsmush, unwrap, update, upperCase, words };
@@ -0,0 +1,24 @@
1
+ import { noop } from "../internal/function/misc.js";
2
+ import { TIMER_DEBOUNCE, TIMER_THROTTLE, getTimer } from "../internal/function/timer.js";
3
+ import { memoize } from "./memoize.js";
4
+ /**
5
+ * Debounce a function, ensuring it is only called after `time` milliseconds have passed
6
+ *
7
+ * On subsequent calls, the timer is reset and will wait another `time` milliseconds _(and so on...)_
8
+ * @param callback Callback to debounce
9
+ * @param time Time in milliseconds to wait before calling the callback _(defaults to match frame rate)_
10
+ * @returns Debounced callback with a `cancel` method
11
+ */
12
+ function debounce(callback, time) {
13
+ return getTimer(TIMER_DEBOUNCE, callback, time);
14
+ }
15
+ /**
16
+ * Throttle a function, ensuring it is only called once every `time` milliseconds
17
+ * @param callback Callback to throttle
18
+ * @param time Time in milliseconds to wait before calling the callback again _(defaults to match frame rate)_
19
+ * @returns Throttled callback with a `cancel` method
20
+ */
21
+ function throttle(callback, time) {
22
+ return getTimer(TIMER_THROTTLE, callback, time);
23
+ }
24
+ export { debounce, memoize, noop, throttle };
package/dist/index.js CHANGED
@@ -36,11 +36,9 @@ import { rgbToHex, rgbToHsl, rgbToHsla } from "./color/space/rgb.js";
36
36
  import { getNormalizedHex, hexToHsl, hexToHsla, hexToRgb, hexToRgba } from "./color/space/hex.js";
37
37
  import { hslToHex, hslToRgb, hslToRgba } from "./color/space/hsl.js";
38
38
  import { getColor } from "./color/index.js";
39
- import frame_rate_default from "./internal/frame-rate.js";
40
- import { debounce } from "./function/debounce.js";
41
39
  import { SizedMap } from "./sized/map.js";
42
40
  import { memoize } from "./function/memoize.js";
43
- import { throttle } from "./function/throttle.js";
41
+ import { debounce, throttle } from "./function/index.js";
44
42
  import { isError, isOk, isResult } from "./internal/result.js";
45
43
  import { flow, pipe } from "./function/work.js";
46
44
  import { equal } from "./internal/value/equal.js";
@@ -72,4 +70,4 @@ import { QueueError, queue } from "./queue.js";
72
70
  import { getRandomBoolean, getRandomCharacters, getRandomColor, getRandomHex, getRandomItem, getRandomItems } from "./random.js";
73
71
  import { attempt } from "./result/index.js";
74
72
  import { SizedSet } from "./sized/set.js";
75
- export { CancelablePromise, frame_rate_default as FRAME_RATE_MS, PromiseTimeoutError, QueueError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, fromQuery, toPromise as fromResult, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, smush, snakeCase, sort, splice, startsWith, sum, template, throttle, timed, times, titleCase, toMap, toPromise, toQuery, toRecord, toResult, toSet, toggle, trim, truncate, tryDecode, tryEncode, unique, unsmush, unwrap, update, upperCase, words };
73
+ export { CancelablePromise, PromiseTimeoutError, QueueError, SizedMap, SizedSet, attempt, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, count, debounce, delay, diff, endsWith, equal, error, exists, filter, find, flatten, floor, flow, toResult as fromPromise, fromQuery, toPromise as fromResult, getArray, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getString, getUuid, getValue, groupBy, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, includes, indexOf, insert, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonNullable, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isTypedArray, join, kebabCase, logger, lowerCase, max, median, memoize, merge, min, noop, ok, omit, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, shuffle, smush, snakeCase, sort, splice, startsWith, sum, template, throttle, timed, times, titleCase, toMap, toPromise, toQuery, toRecord, toResult, toSet, toggle, trim, truncate, tryDecode, tryEncode, unique, unsmush, unwrap, update, upperCase, words };
@@ -0,0 +1,31 @@
1
+ function getInterval(value) {
2
+ return typeof value === "number" && value > 0 ? value : 0;
3
+ }
4
+ function getTimer(type, callback, time) {
5
+ const interval = getInterval(time);
6
+ function run(now) {
7
+ start ??= now;
8
+ if (interval === 0 || now - start >= interval - OFFSET) {
9
+ if (throttle) start = now;
10
+ callback(...args);
11
+ } else frame = requestAnimationFrame(run);
12
+ }
13
+ const throttle = type === TIMER_THROTTLE;
14
+ let args;
15
+ let frame;
16
+ let start;
17
+ const timer = (...parameters) => {
18
+ timer.cancel();
19
+ args = parameters;
20
+ frame = requestAnimationFrame(run);
21
+ };
22
+ timer.cancel = () => {
23
+ cancelAnimationFrame(frame);
24
+ };
25
+ return timer;
26
+ }
27
+ var OFFSET = 5;
28
+ const TIMER_DEBOUNCE = "debounce";
29
+ const TIMER_THROTTLE = "throttle";
30
+ const TIMER_WAIT = "wait";
31
+ export { TIMER_DEBOUNCE, TIMER_THROTTLE, TIMER_WAIT, getTimer };
@@ -1,3 +1,4 @@
1
+ import { TIMER_WAIT, getTimer } from "../internal/function/timer.js";
1
2
  import { PROMISE_ABORT_OPTIONS } from "./models.js";
2
3
  import { getPromiseOptions } from "./helpers.js";
3
4
  import { settlePromise } from "./misc.js";
@@ -5,24 +6,20 @@ function delay(options) {
5
6
  const { signal, time } = getPromiseOptions(options);
6
7
  if (signal?.aborted ?? false) return Promise.reject(signal.reason);
7
8
  function abort() {
8
- cancelAnimationFrame(frame);
9
+ timer.cancel();
9
10
  rejector(signal.reason);
10
11
  }
11
- function run(now) {
12
- start ??= now;
13
- if (now - start >= time - 5) settlePromise(abort, resolver, void 0, signal);
14
- else frame = requestAnimationFrame(run);
15
- }
12
+ const timer = getTimer(TIMER_WAIT, () => {
13
+ settlePromise(abort, resolver, void 0, signal);
14
+ }, time);
16
15
  signal?.addEventListener("abort", abort, PROMISE_ABORT_OPTIONS);
17
- let frame;
18
16
  let rejector;
19
17
  let resolver;
20
- let start;
21
18
  return new Promise((resolve, reject) => {
22
19
  rejector = reject;
23
20
  resolver = resolve;
24
21
  if (time === 0) settlePromise(abort, resolve, void 0, signal);
25
- else frame = requestAnimationFrame(run);
22
+ else timer();
26
23
  });
27
24
  }
28
25
  export { delay };
@@ -1,25 +1,22 @@
1
+ import { TIMER_WAIT, getTimer } from "../internal/function/timer.js";
1
2
  import { PROMISE_ABORT_OPTIONS, PROMISE_EVENT_NAME, PROMISE_MESSAGE_EXPECTATION_TIMED, PromiseTimeoutError } from "./models.js";
2
3
  import { getPromiseOptions } from "./helpers.js";
3
4
  import { settlePromise } from "./misc.js";
4
5
  async function getTimedPromise(promise, time, signal) {
5
6
  function abort() {
6
- cancelAnimationFrame(frame);
7
+ timer.cancel();
7
8
  rejector(signal.reason);
8
9
  }
9
- function run(now) {
10
- start ??= now;
11
- if (time === 0 || now - start >= time - 5) settlePromise(abort, rejector, new PromiseTimeoutError(), signal);
12
- else frame = requestAnimationFrame(run);
13
- }
14
10
  signal?.addEventListener(PROMISE_EVENT_NAME, abort, PROMISE_ABORT_OPTIONS);
15
- let frame;
11
+ const timer = getTimer(TIMER_WAIT, () => {
12
+ settlePromise(abort, rejector, new PromiseTimeoutError(), signal);
13
+ }, time);
16
14
  let rejector;
17
- let start;
18
15
  return Promise.race([promise, new Promise((_, reject) => {
19
16
  rejector = reject;
20
- frame = requestAnimationFrame(run);
17
+ timer();
21
18
  })]).then((value) => {
22
- cancelAnimationFrame(frame);
19
+ timer.cancel();
23
20
  signal?.removeEventListener(PROMISE_EVENT_NAME, abort);
24
21
  return value;
25
22
  });
package/package.json CHANGED
@@ -50,26 +50,10 @@
50
50
  "types": "./types/color/index.d.ts",
51
51
  "default": "./dist/color/index.js"
52
52
  },
53
- "./frame-rate": {
54
- "types": "./types/internal/frame-rate.d.ts",
55
- "default": "./dist/internal/frame-rate.js"
56
- },
57
- "./function/debounce": {
58
- "types": "./types/function/debounce.d.ts",
59
- "default": "./dist/function/debounce.js"
60
- },
61
- "./function/memoize": {
53
+ "./function": {
62
54
  "types": "./types/function/memoize.d.ts",
63
55
  "default": "./dist/function/memoize.js"
64
56
  },
65
- "./function/misc": {
66
- "types": "./types/function/misc.d.ts",
67
- "default": "./dist/function/misc.js"
68
- },
69
- "./function/throttle": {
70
- "types": "./types/function/throttle.d.ts",
71
- "default": "./dist/function/throttle.js"
72
- },
73
57
  "./function/work": {
74
58
  "types": "./types/function/work.d.ts",
75
59
  "default": "./dist/function/work.js"
@@ -192,5 +176,5 @@
192
176
  },
193
177
  "type": "module",
194
178
  "types": "./types/index.d.ts",
195
- "version": "0.150.0"
179
+ "version": "0.152.0"
196
180
  }
@@ -0,0 +1,41 @@
1
+ import {getTimer, TIMER_DEBOUNCE, TIMER_THROTTLE} from '../internal/function/timer';
2
+ import type {CancelableCallback, GenericCallback} from '../models';
3
+
4
+ // #region Functions
5
+
6
+ /**
7
+ * Debounce a function, ensuring it is only called after `time` milliseconds have passed
8
+ *
9
+ * On subsequent calls, the timer is reset and will wait another `time` milliseconds _(and so on...)_
10
+ * @param callback Callback to debounce
11
+ * @param time Time in milliseconds to wait before calling the callback _(defaults to match frame rate)_
12
+ * @returns Debounced callback with a `cancel` method
13
+ */
14
+ export function debounce<Callback extends GenericCallback>(
15
+ callback: Callback,
16
+ time?: number,
17
+ ): CancelableCallback<Callback> {
18
+ return getTimer(TIMER_DEBOUNCE, callback, time);
19
+ }
20
+
21
+ /**
22
+ * Throttle a function, ensuring it is only called once every `time` milliseconds
23
+ * @param callback Callback to throttle
24
+ * @param time Time in milliseconds to wait before calling the callback again _(defaults to match frame rate)_
25
+ * @returns Throttled callback with a `cancel` method
26
+ */
27
+ export function throttle<Callback extends GenericCallback>(
28
+ callback: Callback,
29
+ time?: number,
30
+ ): CancelableCallback<Callback> {
31
+ return getTimer(TIMER_THROTTLE, callback, time);
32
+ }
33
+
34
+ // #endregion
35
+
36
+ // #region Exports
37
+
38
+ export {noop} from '../internal/function/misc';
39
+ export {memoize, type Memoized, type MemoizedOptions} from './memoize';
40
+
41
+ // #endregion
package/src/index.ts CHANGED
@@ -1,14 +1,10 @@
1
- import FRAME_RATE_MS from './internal/frame-rate';
2
-
3
1
  export * from './array/group-by';
4
2
  export * from './array/misc';
5
3
  export * from './array/to-map';
6
4
  export * from './array/to-record';
7
5
  export * from './array/unique';
8
6
 
9
- export * from './function/debounce';
10
- export * from './function/memoize';
11
- export * from './function/throttle';
7
+ export * from './function/index';
12
8
  export * from './function/work';
13
9
 
14
10
  export * from './internal/function/misc';
@@ -45,5 +41,3 @@ export * from './random';
45
41
  export * from './result/index';
46
42
  export * from './sized/map';
47
43
  export * from './sized/set';
48
-
49
- export {FRAME_RATE_MS};
@@ -0,0 +1,68 @@
1
+ import type {CancelableCallback, GenericCallback} from '../../models';
2
+
3
+ // #region Types
4
+
5
+ type TimerType = 'debounce' | 'throttle' | 'wait';
6
+
7
+ // #endregion
8
+
9
+ // #region Functions
10
+
11
+ function getInterval(value: unknown): number {
12
+ return typeof value === 'number' && value > 0 ? value : 0;
13
+ }
14
+
15
+ export function getTimer<Callback extends GenericCallback>(
16
+ type: TimerType,
17
+ callback: Callback,
18
+ time?: number,
19
+ ): CancelableCallback<Callback> {
20
+ const interval = getInterval(time);
21
+
22
+ function run(now: DOMHighResTimeStamp): void {
23
+ start ??= now;
24
+
25
+ if (interval === 0 || now - start >= interval - OFFSET) {
26
+ if (throttle) {
27
+ start = now;
28
+ }
29
+
30
+ callback(...args);
31
+ } else {
32
+ frame = requestAnimationFrame(run);
33
+ }
34
+ }
35
+
36
+ const throttle = type === TIMER_THROTTLE;
37
+
38
+ let args: Parameters<Callback>;
39
+ let frame: DOMHighResTimeStamp | undefined;
40
+ let start: DOMHighResTimeStamp;
41
+
42
+ const timer = (...parameters: Parameters<Callback>): void => {
43
+ timer.cancel();
44
+
45
+ args = parameters;
46
+ frame = requestAnimationFrame(run);
47
+ };
48
+
49
+ timer.cancel = (): void => {
50
+ cancelAnimationFrame(frame!);
51
+ };
52
+
53
+ return timer as CancelableCallback<Callback>;
54
+ }
55
+
56
+ // #endregion
57
+
58
+ // #region Variables
59
+
60
+ const OFFSET = 5;
61
+
62
+ export const TIMER_DEBOUNCE: TimerType = 'debounce';
63
+
64
+ export const TIMER_THROTTLE: TimerType = 'throttle';
65
+
66
+ export const TIMER_WAIT: TimerType = 'wait';
67
+
68
+ // #endregion
@@ -1,3 +1,4 @@
1
+ import {getTimer, TIMER_WAIT} from '../internal/function/timer';
1
2
  import {getPromiseOptions} from './helpers';
2
3
  import {settlePromise} from './misc';
3
4
  import {PROMISE_ABORT_OPTIONS, type PromiseOptions} from './models';
@@ -26,27 +27,23 @@ export function delay(options?: unknown): Promise<void> {
26
27
  }
27
28
 
28
29
  function abort(): void {
29
- cancelAnimationFrame(frame);
30
+ timer.cancel();
30
31
 
31
32
  rejector(signal!.reason);
32
33
  }
33
34
 
34
- function run(now: DOMHighResTimeStamp): void {
35
- start ??= now;
36
-
37
- if (now - start >= time - 5) {
35
+ const timer = getTimer(
36
+ TIMER_WAIT,
37
+ () => {
38
38
  settlePromise(abort, resolver, undefined, signal);
39
- } else {
40
- frame = requestAnimationFrame(run);
41
- }
42
- }
39
+ },
40
+ time,
41
+ );
43
42
 
44
43
  signal?.addEventListener('abort', abort, PROMISE_ABORT_OPTIONS);
45
44
 
46
- let frame: DOMHighResTimeStamp;
47
45
  let rejector: (reason: unknown) => void;
48
46
  let resolver: () => void;
49
- let start: DOMHighResTimeStamp;
50
47
 
51
48
  return new Promise((resolve, reject) => {
52
49
  rejector = reject;
@@ -55,7 +52,7 @@ export function delay(options?: unknown): Promise<void> {
55
52
  if (time === 0) {
56
53
  settlePromise(abort, resolve, undefined, signal);
57
54
  } else {
58
- frame = requestAnimationFrame(run);
55
+ timer();
59
56
  }
60
57
  });
61
58
  }
@@ -1,3 +1,4 @@
1
+ import {getTimer, TIMER_WAIT} from '../internal/function/timer';
1
2
  import type {RequiredKeys} from '../models';
2
3
  import {getPromiseOptions} from './helpers';
3
4
  import {settlePromise} from './misc';
@@ -17,36 +18,32 @@ export async function getTimedPromise<Value>(
17
18
  signal?: AbortSignal,
18
19
  ): Promise<Value> {
19
20
  function abort(): void {
20
- cancelAnimationFrame(frame);
21
+ timer.cancel();
21
22
 
22
23
  rejector(signal!.reason);
23
24
  }
24
25
 
25
- function run(now: DOMHighResTimeStamp): void {
26
- start ??= now;
26
+ signal?.addEventListener(PROMISE_EVENT_NAME, abort, PROMISE_ABORT_OPTIONS);
27
27
 
28
- if (time === 0 || now - start >= time - 5) {
28
+ const timer = getTimer(
29
+ TIMER_WAIT,
30
+ () => {
29
31
  settlePromise(abort, rejector, new PromiseTimeoutError(), signal);
30
- } else {
31
- frame = requestAnimationFrame(run);
32
- }
33
- }
34
-
35
- signal?.addEventListener(PROMISE_EVENT_NAME, abort, PROMISE_ABORT_OPTIONS);
32
+ },
33
+ time,
34
+ );
36
35
 
37
- let frame: DOMHighResTimeStamp;
38
36
  let rejector: (reason: unknown) => void;
39
- let start: DOMHighResTimeStamp;
40
37
 
41
38
  return Promise.race<Value>([
42
39
  promise,
43
40
  new Promise((_, reject) => {
44
41
  rejector = reject;
45
42
 
46
- frame = requestAnimationFrame(run);
43
+ timer();
47
44
  }),
48
45
  ]).then(value => {
49
- cancelAnimationFrame(frame);
46
+ timer.cancel();
50
47
 
51
48
  signal?.removeEventListener(PROMISE_EVENT_NAME, abort);
52
49
 
@@ -8,3 +8,12 @@ import type { CancelableCallback, GenericCallback } from '../models';
8
8
  * @returns Debounced callback with a `cancel` method
9
9
  */
10
10
  export declare function debounce<Callback extends GenericCallback>(callback: Callback, time?: number): CancelableCallback<Callback>;
11
+ /**
12
+ * Throttle a function, ensuring it is only called once every `time` milliseconds
13
+ * @param callback Callback to throttle
14
+ * @param time Time in milliseconds to wait before calling the callback again _(defaults to match frame rate)_
15
+ * @returns Throttled callback with a `cancel` method
16
+ */
17
+ export declare function throttle<Callback extends GenericCallback>(callback: Callback, time?: number): CancelableCallback<Callback>;
18
+ export { noop } from '../internal/function/misc';
19
+ export { memoize, type Memoized, type MemoizedOptions } from './memoize';
package/types/index.d.ts CHANGED
@@ -1,12 +1,9 @@
1
- import FRAME_RATE_MS from './internal/frame-rate';
2
1
  export * from './array/group-by';
3
2
  export * from './array/misc';
4
3
  export * from './array/to-map';
5
4
  export * from './array/to-record';
6
5
  export * from './array/unique';
7
- export * from './function/debounce';
8
- export * from './function/memoize';
9
- export * from './function/throttle';
6
+ export * from './function/index';
10
7
  export * from './function/work';
11
8
  export * from './internal/function/misc';
12
9
  export * from './internal/string';
@@ -39,4 +36,3 @@ export * from './random';
39
36
  export * from './result/index';
40
37
  export * from './sized/map';
41
38
  export * from './sized/set';
42
- export { FRAME_RATE_MS };
@@ -0,0 +1,7 @@
1
+ import type { CancelableCallback, GenericCallback } from '../../models';
2
+ type TimerType = 'debounce' | 'throttle' | 'wait';
3
+ export declare function getTimer<Callback extends GenericCallback>(type: TimerType, callback: Callback, time?: number): CancelableCallback<Callback>;
4
+ export declare const TIMER_DEBOUNCE: TimerType;
5
+ export declare const TIMER_THROTTLE: TimerType;
6
+ export declare const TIMER_WAIT: TimerType;
7
+ export {};
@@ -1,13 +0,0 @@
1
- import { getLimiter } from "../internal/function/limiter.js";
2
- /**
3
- * Debounce a function, ensuring it is only called after `time` milliseconds have passed
4
- *
5
- * On subsequent calls, the timer is reset and will wait another `time` milliseconds _(and so on...)_
6
- * @param callback Callback to debounce
7
- * @param time Time in milliseconds to wait before calling the callback _(defaults to match frame rate)_
8
- * @returns Debounced callback with a `cancel` method
9
- */
10
- function debounce(callback, time) {
11
- return getLimiter(callback, false, time);
12
- }
13
- export { debounce };
@@ -1,2 +0,0 @@
1
- import { noop } from "../internal/function/misc.js";
2
- export { noop };
@@ -1,11 +0,0 @@
1
- import { getLimiter } from "../internal/function/limiter.js";
2
- /**
3
- * Throttle a function, ensuring it is only called once every `time` milliseconds
4
- * @param callback Callback to throttle
5
- * @param time Time in milliseconds to wait before calling the callback again _(defaults to match frame rate)_
6
- * @returns Throttled callback with a `cancel` method
7
- */
8
- function throttle(callback, time) {
9
- return getLimiter(callback, true, time);
10
- }
11
- export { throttle };
@@ -1,25 +0,0 @@
1
- function calculate() {
2
- return new Promise((resolve) => {
3
- const values = [];
4
- let last;
5
- function step(now) {
6
- if (last != null) values.push(now - last);
7
- last = now;
8
- if (values.length >= TOTAL) resolve(values.sort().slice(TRIM_PART, -TRIM_PART).reduce((first, second) => first + second, 0) / (values.length - TRIM_TOTAL));
9
- else requestAnimationFrame(step);
10
- }
11
- requestAnimationFrame(step);
12
- });
13
- }
14
- var TOTAL = 10;
15
- var TRIM_PART = 2;
16
- var TRIM_TOTAL = 4;
17
- var FRAME_RATE_MS = 1e3 / 60;
18
- /**
19
- * A calculated average of the refresh rate of the display _(in milliseconds)_
20
- */
21
- calculate().then((value) => {
22
- FRAME_RATE_MS = value;
23
- });
24
- var frame_rate_default = FRAME_RATE_MS;
25
- export { frame_rate_default as default };
@@ -1,29 +0,0 @@
1
- import frame_rate_default from "../frame-rate.js";
2
- function getLimiter(callback, throttler, time) {
3
- const interval = typeof time === "number" && time >= frame_rate_default ? time : frame_rate_default;
4
- function step(now, parameters) {
5
- if (interval === frame_rate_default || now - timestamp >= interval) {
6
- if (throttler) timestamp = now;
7
- callback(...parameters);
8
- } else frame = requestAnimationFrame((next) => {
9
- step(next, parameters);
10
- });
11
- }
12
- let frame;
13
- let timestamp;
14
- const limiter = (...parameters) => {
15
- limiter.cancel();
16
- frame = requestAnimationFrame((now) => {
17
- timestamp ??= now - frame_rate_default;
18
- step(now, parameters);
19
- });
20
- };
21
- limiter.cancel = () => {
22
- if (frame != null) {
23
- cancelAnimationFrame(frame);
24
- frame = void 0;
25
- }
26
- };
27
- return limiter;
28
- }
29
- export { getLimiter };
@@ -1,21 +0,0 @@
1
- import {getLimiter} from '../internal/function/limiter';
2
- import type {CancelableCallback, GenericCallback} from '../models';
3
-
4
- // #region Functions
5
-
6
- /**
7
- * Debounce a function, ensuring it is only called after `time` milliseconds have passed
8
- *
9
- * On subsequent calls, the timer is reset and will wait another `time` milliseconds _(and so on...)_
10
- * @param callback Callback to debounce
11
- * @param time Time in milliseconds to wait before calling the callback _(defaults to match frame rate)_
12
- * @returns Debounced callback with a `cancel` method
13
- */
14
- export function debounce<Callback extends GenericCallback>(
15
- callback: Callback,
16
- time?: number,
17
- ): CancelableCallback<Callback> {
18
- return getLimiter(callback, false, time);
19
- }
20
-
21
- // #endregion
@@ -1 +0,0 @@
1
- export {noop} from '../internal/function/misc';
@@ -1,19 +0,0 @@
1
- import {getLimiter} from '../internal/function/limiter';
2
- import type {CancelableCallback, GenericCallback} from '../models';
3
-
4
- // #region Functions
5
-
6
- /**
7
- * Throttle a function, ensuring it is only called once every `time` milliseconds
8
- * @param callback Callback to throttle
9
- * @param time Time in milliseconds to wait before calling the callback again _(defaults to match frame rate)_
10
- * @returns Throttled callback with a `cancel` method
11
- */
12
- export function throttle<Callback extends GenericCallback>(
13
- callback: Callback,
14
- time?: number,
15
- ): CancelableCallback<Callback> {
16
- return getLimiter(callback, true, time);
17
- }
18
-
19
- // #endregion
@@ -1,63 +0,0 @@
1
- // #region Functions
2
-
3
- function calculate(): Promise<number> {
4
- return new Promise(resolve => {
5
- const values: number[] = [];
6
-
7
- let last: DOMHighResTimeStamp;
8
-
9
- function step(now: DOMHighResTimeStamp): void {
10
- if (last != null) {
11
- values.push(now - last);
12
- }
13
-
14
- last = now;
15
-
16
- if (values.length >= TOTAL) {
17
- const median =
18
- values
19
- .sort()
20
- .slice(TRIM_PART, -TRIM_PART)
21
- .reduce((first, second) => first + second, 0) /
22
- (values.length - TRIM_TOTAL);
23
-
24
- resolve(median);
25
- } else {
26
- requestAnimationFrame(step);
27
- }
28
- }
29
-
30
- requestAnimationFrame(step);
31
- });
32
- }
33
-
34
- // #endregion
35
-
36
- // #region Variables
37
-
38
- const TOTAL = 10;
39
-
40
- const TRIM_PART = 2;
41
-
42
- const TRIM_TOTAL = 4;
43
-
44
- let FRAME_RATE_MS = 1000 / 60;
45
-
46
- // #endregion
47
-
48
- // #region Initialization
49
-
50
- /**
51
- * A calculated average of the refresh rate of the display _(in milliseconds)_
52
- */
53
- calculate().then(value => {
54
- FRAME_RATE_MS = value;
55
- });
56
-
57
- // #endregion
58
-
59
- // #region Exports
60
-
61
- export default FRAME_RATE_MS;
62
-
63
- // #endregion
@@ -1,52 +0,0 @@
1
- import type {CancelableCallback} from '../../models';
2
- import type {GenericCallback} from '../../models';
3
- import FRAME_RATE_MS from '../frame-rate';
4
-
5
- // #region Functions
6
-
7
- export function getLimiter<Callback extends GenericCallback>(
8
- callback: Callback,
9
- throttler: boolean,
10
- time?: number,
11
- ): CancelableCallback<Callback> {
12
- const interval = typeof time === 'number' && time >= FRAME_RATE_MS ? time : FRAME_RATE_MS;
13
-
14
- function step(now: DOMHighResTimeStamp, parameters: Parameters<Callback>): void {
15
- if (interval === FRAME_RATE_MS || now - timestamp >= interval) {
16
- if (throttler) {
17
- timestamp = now;
18
- }
19
-
20
- callback(...parameters);
21
- } else {
22
- frame = requestAnimationFrame(next => {
23
- step(next, parameters);
24
- });
25
- }
26
- }
27
-
28
- let frame: DOMHighResTimeStamp | undefined;
29
- let timestamp: DOMHighResTimeStamp;
30
-
31
- const limiter = (...parameters: Parameters<Callback>): void => {
32
- limiter.cancel();
33
-
34
- frame = requestAnimationFrame(now => {
35
- timestamp ??= now - FRAME_RATE_MS;
36
-
37
- step(now, parameters);
38
- });
39
- };
40
-
41
- limiter.cancel = (): void => {
42
- if (frame != null) {
43
- cancelAnimationFrame(frame);
44
-
45
- frame = undefined;
46
- }
47
- };
48
-
49
- return limiter as CancelableCallback<Callback>;
50
- }
51
-
52
- // #endregion
@@ -1 +0,0 @@
1
- export { noop } from '../internal/function/misc';
@@ -1,8 +0,0 @@
1
- import type { CancelableCallback, GenericCallback } from '../models';
2
- /**
3
- * Throttle a function, ensuring it is only called once every `time` milliseconds
4
- * @param callback Callback to throttle
5
- * @param time Time in milliseconds to wait before calling the callback again _(defaults to match frame rate)_
6
- * @returns Throttled callback with a `cancel` method
7
- */
8
- export declare function throttle<Callback extends GenericCallback>(callback: Callback, time?: number): CancelableCallback<Callback>;
@@ -1,2 +0,0 @@
1
- declare let FRAME_RATE_MS: number;
2
- export default FRAME_RATE_MS;
@@ -1,3 +0,0 @@
1
- import type { CancelableCallback } from '../../models';
2
- import type { GenericCallback } from '../../models';
3
- export declare function getLimiter<Callback extends GenericCallback>(callback: Callback, throttler: boolean, time?: number): CancelableCallback<Callback>;