time-queues 1.0.5 → 1.1.1

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.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * A microtask that will be executed when scheduled.
3
+ */
4
+ export declare class MicroTask {
5
+ /**
6
+ * The function to execute when the microtask is scheduled.
7
+ */
8
+ fn: () => void;
9
+
10
+ /**
11
+ * Creates a new microtask.
12
+ * @param fn The function to execute when the microtask is scheduled.
13
+ */
14
+ constructor(fn: () => void);
15
+ }
16
+
17
+ export default MicroTask;
package/src/MicroTask.js CHANGED
@@ -1,3 +1,5 @@
1
+ // @ts-self-types="./MicroTask.d.ts"
2
+
1
3
  'use strict';
2
4
 
3
5
  export class MicroTask {
@@ -0,0 +1,63 @@
1
+ import {MicroTask} from './MicroTask';
2
+
3
+ /**
4
+ * A queue of microtasks that will be executed when scheduled.
5
+ * It is a base class for other task queues.
6
+ */
7
+ export declare class MicroTaskQueue {
8
+ /**
9
+ * Whether the queue is paused.
10
+ */
11
+ paused: boolean;
12
+
13
+ /**
14
+ * Creates a new microtask queue.
15
+ * @param paused Whether the queue should start paused.
16
+ */
17
+ constructor(paused?: boolean);
18
+
19
+ /**
20
+ * Whether the queue is empty.
21
+ * It is meant to be overridden in subclasses.
22
+ */
23
+ get isEmpty(): boolean;
24
+
25
+ /**
26
+ * Enqueues a microtask.
27
+ * It is meant to be overridden in subclasses.
28
+ * @param fn The function to execute when the microtask is scheduled.
29
+ * @returns The enqueued microtask.
30
+ */
31
+ enqueue(fn: () => void): MicroTask;
32
+
33
+ /**
34
+ * Dequeues a microtask.
35
+ * It is meant to be overridden in subclasses.
36
+ * @param task The microtask to dequeue.
37
+ * @returns The queue.
38
+ */
39
+ dequeue(task: MicroTask): this;
40
+
41
+ /**
42
+ * Clears the queue.
43
+ * It is meant to be overridden in subclasses.
44
+ * @returns The queue.
45
+ */
46
+ clear(): this;
47
+
48
+ /**
49
+ * Pauses the queue.
50
+ * It is meant to be overridden in subclasses.
51
+ * @returns The queue.
52
+ */
53
+ pause(): this;
54
+
55
+ /**
56
+ * Resumes the queue.
57
+ * It is meant to be overridden in subclasses.
58
+ * @returns The queue.
59
+ */
60
+ resume(): this;
61
+ }
62
+
63
+ export default MicroTaskQueue;
@@ -1,3 +1,5 @@
1
+ // @ts-self-types="./MicroTaskQueue.d.ts"
2
+
1
3
  'use strict';
2
4
 
3
5
  import MicroTask from './MicroTask.js';
@@ -0,0 +1,87 @@
1
+ import {ListQueue, Task} from './ListQueue';
2
+
3
+ /**
4
+ * A page state.
5
+ */
6
+ export declare type PageState = 'active' | 'passive' | 'hidden' | 'frozen' | 'terminated';
7
+
8
+ /**
9
+ * A queue that monitors the page's lifecycle.
10
+ */
11
+ export declare class PageWatcher extends ListQueue {
12
+ /**
13
+ * Whether the queue is paused.
14
+ */
15
+ paused: boolean;
16
+
17
+ /**
18
+ * The current state of the page.
19
+ */
20
+ currentState: PageState;
21
+
22
+ /**
23
+ * Creates a new page watcher.
24
+ * @param started Whether the queue is unpaused initially.
25
+ */
26
+ constructor(started?: boolean);
27
+
28
+ /**
29
+ * Whether the queue is empty.
30
+ */
31
+ get isEmpty(): boolean;
32
+
33
+ /**
34
+ * Enqueues a task.
35
+ * @param fn The function to execute.
36
+ * @param initialize Whether the function should be executed before the state changes.
37
+ * @returns The task object.
38
+ */
39
+ enqueue(
40
+ fn: (state: PageState, previousState: PageState, task: Task, queue: ListQueue) => void,
41
+ initialize?: boolean
42
+ ): Task;
43
+
44
+ /**
45
+ * Dequeues a task.
46
+ * @param task The task to dequeue.
47
+ * @returns The queue.
48
+ */
49
+ dequeue(task: Task): this;
50
+
51
+ /**
52
+ * Clears the queue.
53
+ * @returns The queue.
54
+ */
55
+ clear(): this;
56
+
57
+ /**
58
+ * Pauses the queue.
59
+ * @returns The queue.
60
+ */
61
+ pause(): this;
62
+
63
+ /**
64
+ * Resumes the queue.
65
+ * @returns The queue.
66
+ */
67
+ resume(): this;
68
+ }
69
+
70
+ /**
71
+ * A function that pauses or resumes a queue based on the page's state.
72
+ * Its result can be used as a callback for `PageWatcher`.
73
+ * @param queue The queue to pause or resume.
74
+ * @param resumeStatesList The list of states that should resume the queue.
75
+ * @returns A function that can be called with the current page state.
76
+ */
77
+ export declare const watchStates: (
78
+ queue: ListQueue,
79
+ resumeStatesList: PageState[] = ['active']
80
+ ) => (state: PageState) => void;
81
+
82
+ /**
83
+ * The default page watcher usually used as a global queue.
84
+ */
85
+ export declare const pageWatcher: PageWatcher;
86
+
87
+ export default PageWatcher;
@@ -1,3 +1,5 @@
1
+ // @ts-self-types="./PageWatcher.d.ts"
2
+
1
3
  'use strict';
2
4
 
3
5
  import ListQueue from './ListQueue.js';
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The options for a retainer.
3
+ */
4
+ export declare interface RetainerOptions<T = unknown> {
5
+ /**
6
+ * The function to create a value.
7
+ */
8
+ create: () => Promise<T> | T;
9
+ /**
10
+ * The function to destroy a value.
11
+ */
12
+ destroy: (value: T) => Promise<void> | void;
13
+ /**
14
+ * The retention period in milliseconds.
15
+ */
16
+ retentionPeriod: number;
17
+ }
18
+
19
+ /**
20
+ * Retains a value for a specified time.
21
+ */
22
+ export declare class Retainer<T = unknown> implements RetainerOptions<T> {
23
+ /**
24
+ * The counter for the number of active references.
25
+ */
26
+ counter: number;
27
+
28
+ /**
29
+ * The value currently retained.
30
+ */
31
+ value: T | null;
32
+
33
+ /**
34
+ * The function to create a value.
35
+ */
36
+ create: () => Promise<T> | T;
37
+
38
+ /**
39
+ * The function to destroy a value.
40
+ */
41
+ destroy: (value: T) => Promise<void> | void;
42
+
43
+ /**
44
+ * The retention period in milliseconds.
45
+ */
46
+ retentionPeriod: number;
47
+
48
+ /**
49
+ * Creates a new retainer.
50
+ * @param options The options for the retainer.
51
+ */
52
+ constructor(options: RetainerOptions<T>);
53
+
54
+ /**
55
+ * Retrieves the retained value.
56
+ * @returns The retained value as a promise.
57
+ */
58
+ async get(): Promise<T>;
59
+
60
+ /**
61
+ * Releases the retained value.
62
+ * @param immediately Whether to release the value immediately. Otherwise it'll be retained for the retention period.
63
+ * @returns The retainer object.
64
+ */
65
+ async release(immediately?: boolean): Promise<this>;
66
+ }
67
+
68
+ export default Retainer;
@@ -0,0 +1,48 @@
1
+ // @ts-self-types="./Retainer.d.ts"
2
+
3
+ 'use strict';
4
+
5
+ export class Retainer {
6
+ constructor({create, destroy, retentionPeriod = 1_000}) {
7
+ if (!create || !destroy) throw new Error('Retainer: create and destroy are required');
8
+ this.create = create;
9
+ this.destroy = destroy;
10
+ this.retentionPeriod = retentionPeriod;
11
+ this.counter = 0;
12
+ this.handle = null;
13
+ this.value = null;
14
+ }
15
+
16
+ async get() {
17
+ if (!this.counter) {
18
+ if (this.handle) {
19
+ clearTimeout(this.handle);
20
+ this.handle = null;
21
+ } else {
22
+ this.value = await this.create();
23
+ }
24
+ }
25
+ ++this.counter;
26
+ return this.value;
27
+ }
28
+
29
+ async release(immediately) {
30
+ if (this.counter <= 0) throw new Error('Retainer: counter is already zero');
31
+ if (--this.counter) return this;
32
+ if (immediately) {
33
+ const value = this.value;
34
+ this.value = null;
35
+ await this.destroy(value);
36
+ return this;
37
+ }
38
+ this.handle = setTimeout(async () => {
39
+ const value = this.value;
40
+ this.value = null;
41
+ this.handle = null;
42
+ await this.destroy(value);
43
+ }, this.retentionPeriod);
44
+ return this;
45
+ }
46
+ }
47
+
48
+ export default Retainer;
@@ -0,0 +1,119 @@
1
+ import MicroTask from './MicroTask';
2
+ import MicroTaskQueue from './MicroTaskQueue';
3
+
4
+ /**
5
+ * A task that will be executed at a later time by `Scheduler`.
6
+ */
7
+ export declare class Task extends MicroTask {
8
+ /**
9
+ * The function to execute.
10
+ */
11
+ fn: (task: Task, scheduler: Scheduler) => void;
12
+
13
+ /**
14
+ * The time in milliseconds (Unix timestamp) when the task is scheduled to run.
15
+ */
16
+ time: number;
17
+
18
+ /**
19
+ * The delay in milliseconds before the task is executed.
20
+ */
21
+ delay: number;
22
+
23
+ /**
24
+ * Creates a new task.
25
+ * @param delay The delay before the task is executed. It can be a number of milliseconds or a `Date` object as an absolute time.
26
+ * @param fn The function to execute.
27
+ */
28
+ constructor(delay: number | Date, fn: (task: Task, scheduler: Scheduler) => void);
29
+ }
30
+
31
+ /**
32
+ * A scheduler that manages tasks to be executed at a later time.
33
+ */
34
+ export declare class Scheduler extends MicroTaskQueue {
35
+ /**
36
+ * Whether the scheduler is paused.
37
+ */
38
+ paused: boolean;
39
+
40
+ /**
41
+ * The tolerance for comparing starting time of tasks.
42
+ */
43
+ tolerance: number;
44
+
45
+ /**
46
+ * Creates a new scheduler.
47
+ * @param paused Whether the scheduler should start paused.
48
+ * @param tolerance The tolerance for comparing starting time of tasks.
49
+ */
50
+ constructor(paused?: boolean, tolerance: number = 4);
51
+
52
+ /**
53
+ * Whether the scheduler is empty.
54
+ */
55
+ get isEmpty(): boolean;
56
+
57
+ /**
58
+ * The next scheduled time or `Infinity` if the scheduler is empty.
59
+ */
60
+ get nextTime(): number;
61
+
62
+ /**
63
+ * Enqueues a task.
64
+ * @param fn The function to execute.
65
+ * @param delay The delay before the task is executed. It can be a number of milliseconds or a `Date` object as an absolute time.
66
+ * @returns The task object.
67
+ */
68
+ enqueue(fn: (task: Task, scheduler: Scheduler) => void, delay: number | Date): Task;
69
+
70
+ /**
71
+ * Removes a task from the scheduler.
72
+ * @param task The task to remove.
73
+ * @returns The scheduler object.
74
+ */
75
+ dequeue(task: Task): this;
76
+
77
+ /**
78
+ * Creates a promise that resolves after a delay.
79
+ * @param delay The delay before the task is executed. It can be a number of milliseconds or a `Date` object as an absolute time.
80
+ * @returns A promise that resolves after the delay.
81
+ */
82
+ schedule(delay: number | Date): Promise<{task: Task; scheduler: Scheduler}>;
83
+
84
+ /**
85
+ * Clears the queue.
86
+ * @returns The queue.
87
+ */
88
+ clear(): this;
89
+
90
+ /**
91
+ * Pauses the queue.
92
+ * @returns The queue.
93
+ */
94
+ pause(): this;
95
+
96
+ /**
97
+ * Resumes the queue.
98
+ * @returns The queue.
99
+ */
100
+ resume(): this;
101
+ }
102
+
103
+ /**
104
+ * Creates a repeatable task.
105
+ * @param fn The function to execute.
106
+ * @param delay The delay before the task is executed. It can be a number of milliseconds or a `Date` object as an absolute time.
107
+ * @returns A function that can be used to enqueue the task to the scheduler.
108
+ */
109
+ export declare const repeat: (
110
+ fn: (task: Task, scheduler: Scheduler) => void,
111
+ delay: number | Date
112
+ ) => (task: Task, scheduler: Scheduler) => void;
113
+
114
+ /**
115
+ * A scheduler instance usually used as a global scheduler.
116
+ */
117
+ export declare const scheduler: Scheduler;
118
+
119
+ export default scheduler;
package/src/Scheduler.js CHANGED
@@ -1,3 +1,5 @@
1
+ // @ts-self-types="./Scheduler.d.ts"
2
+
1
3
  'use strict';
2
4
 
3
5
  import MinHeap from 'list-toolkit/heap.js';
@@ -81,6 +83,12 @@ export class Scheduler extends MicroTaskQueue {
81
83
  return this;
82
84
  }
83
85
 
86
+ schedule(delay) {
87
+ return new Promise(resolve =>
88
+ this.enqueue((task, scheduler) => resolve({task, scheduler}), delay)
89
+ );
90
+ }
91
+
84
92
  clear() {
85
93
  const paused = this.paused;
86
94
  if (!paused) this.pause();
@@ -113,11 +121,11 @@ export class Scheduler extends MicroTaskQueue {
113
121
  }
114
122
 
115
123
  export const repeat = (fn, delay) => {
116
- const repeatableTask = (task, scheduler) => {
124
+ const repeatableFunction = (task, scheduler) => {
117
125
  fn(task, scheduler);
118
- scheduler.enqueue(repeatableTask, isNaN(delay) ? task.delay : delay);
126
+ scheduler.enqueue(repeatableFunction, isNaN(delay) ? task.delay : delay);
119
127
  };
120
- return repeatableTask;
128
+ return repeatableFunction;
121
129
  };
122
130
 
123
131
  export const scheduler = new Scheduler();
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Options for the throttler.
3
+ */
4
+ export declare type ThrottlerOptions = {
5
+ /**
6
+ * The throttle timeout.
7
+ */
8
+ throttleTimeout?: number;
9
+ /**
10
+ * The timeout for keys that have never been seen.
11
+ */
12
+ neverSeenTimeout?: number;
13
+ /**
14
+ * The vacuum period.
15
+ */
16
+ vacuumPeriod?: number;
17
+ };
18
+
19
+ /**
20
+ * A throttler that throttles the execution of tasks based on a key.
21
+ */
22
+ export declare class Throttler implements ThrottlerOptions {
23
+ /**
24
+ * The throttle timeout.
25
+ */
26
+ throttleTimeout: number;
27
+
28
+ /**
29
+ * The timeout for keys that have never been seen.
30
+ */
31
+ neverSeenTimeout: number;
32
+
33
+ /**
34
+ * The vacuum period.
35
+ */
36
+ vacuumPeriod: number;
37
+
38
+ /**
39
+ * The last seen times for keys.
40
+ */
41
+ lastSeen: Map<unknown, number>;
42
+
43
+ /**
44
+ * Creates a new throttler.
45
+ * @param options The options for the throttler.
46
+ */
47
+ constructor({
48
+ throttleTimeout = 1_000,
49
+ neverSeenTimeout = 0,
50
+ vacuumPeriod = throttleTimeout * 3
51
+ }: ThrottlerOptions = {});
52
+
53
+ /**
54
+ * Retrieves the last seen time for a key as a timestamp in milliseconds.
55
+ * @param key The key to retrieve the last seen time for.
56
+ * @returns The last seen time for the key or `0` if the key has never been seen.
57
+ */
58
+ getLastSeen(key: unknown): number;
59
+
60
+ /**
61
+ * Retrieves the delay for a key.
62
+ * @param key The key to retrieve the delay for.
63
+ * @returns The delay for the key in milliseconds.
64
+ */
65
+ getDelay(key: unknown): number;
66
+
67
+ /**
68
+ * Waits for a key to be available.
69
+ * @param key The key to wait for.
70
+ * @returns A promise that resolves when the key is available.
71
+ */
72
+ wait(key: unknown): Promise<void>;
73
+
74
+ /**
75
+ * Retrieves the vacuuming state.
76
+ * @returns `true` if the vacuuming is active, `false` otherwise.
77
+ */
78
+ get isVacuuming(): boolean;
79
+
80
+ /**
81
+ * Starts the vacuum process.
82
+ * The vacuum process removes keys that expired.
83
+ * @returns The throttler object.
84
+ */
85
+ startVacuum(): this;
86
+
87
+ /**
88
+ * Stops the vacuum process.
89
+ * The vacuum process removes keys that expired.
90
+ * @returns The throttler object.
91
+ */
92
+ stopVacuum(): this;
93
+ }
94
+
95
+ export default Throttler;
@@ -0,0 +1,71 @@
1
+ // @ts-self-types="./Throttler.d.ts"
2
+
3
+ 'use strict';
4
+
5
+ import sleep from './sleep.js';
6
+
7
+ export class Throttler {
8
+ constructor({
9
+ throttleTimeout = 1_000,
10
+ neverSeenTimeout = 0,
11
+ vacuumPeriod = throttleTimeout * 3
12
+ } = {}) {
13
+ this.throttleTimeout = throttleTimeout;
14
+ this.neverSeenTimeout = neverSeenTimeout;
15
+ this.vacuumPeriod = vacuumPeriod;
16
+ this.lastSeen = new Map();
17
+ this.handle = null;
18
+ if (this.vacuumPeriod) this.startVacuum();
19
+ }
20
+
21
+ getLastSeen(key) {
22
+ return this.lastSeen.get(key) ?? 0;
23
+ }
24
+
25
+ getDelay(key) {
26
+ const now = Date.now();
27
+ let lastSeen = now,
28
+ delay = this.neverSeenTimeout;
29
+ if (this.lastSeen.has(key)) {
30
+ lastSeen = this.lastSeen.get(key);
31
+ delay = Math.max(0, this.throttleTimeout - (now - lastSeen));
32
+ }
33
+ this.lastSeen.set(key, lastSeen + delay);
34
+ return delay;
35
+ }
36
+
37
+ async wait(key) {
38
+ const delay = this.getDelay(key);
39
+ if (delay > 0) await sleep(delay);
40
+ }
41
+
42
+ get isVacuuming() {
43
+ return !!this.handle;
44
+ }
45
+
46
+ vacuum() {
47
+ const now = Date.now();
48
+ for (const [key, lastSeen] of this.lastSeen) {
49
+ if (now - lastSeen > this.throttleTimeout) {
50
+ this.lastSeen.delete(key);
51
+ }
52
+ }
53
+ }
54
+
55
+ startVacuum() {
56
+ if (this.handle) return this;
57
+ this.handle = setInterval(() => {
58
+ this.vacuum();
59
+ }, this.vacuumPeriod);
60
+ return this;
61
+ }
62
+
63
+ stopVacuum() {
64
+ if (!this.handle) return this;
65
+ clearInterval(this.handle);
66
+ this.handle = null;
67
+ return this;
68
+ }
69
+ }
70
+
71
+ export default Throttler;
package/src/audit.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Audits a function by ensuring it is not called more often than the specified interval.
3
+ * The first call starts a timeout. At the end of the timeout, the last seen arguments are passed to the function.
4
+ * This function is similar to `throttle()`, but the last seen arguments are passed to the function.
5
+ *
6
+ * @param fn The function to audit.
7
+ * @param ms The minimum time interval between function calls, in milliseconds.
8
+ * @returns An audited version of the function.
9
+ */
10
+ export declare function audit<T extends (...args: any[]) => void>(
11
+ fn: T,
12
+ ms: number
13
+ ): (...args: Parameters<T>) => void;
14
+
15
+ export default audit;
package/src/audit.js ADDED
@@ -0,0 +1,20 @@
1
+ // @ts-self-types="./audit.d.ts"
2
+
3
+ 'use strict';
4
+
5
+ export const audit = (fn, ms) => {
6
+ let handle = null,
7
+ lastSeenArgs = null;
8
+ return (...args) => {
9
+ lastSeenArgs = [...args];
10
+ if (handle) return;
11
+ handle = setTimeout(() => {
12
+ handle = null;
13
+ const args = lastSeenArgs;
14
+ lastSeenArgs = null;
15
+ fn(...args);
16
+ }, ms);
17
+ };
18
+ };
19
+
20
+ export default audit;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Debounces a function by using the last seen arguments after the specified time interval.
3
+ * Every call to the debounced function will reset the timeout.
4
+ *
5
+ * @param fn The function to debounce.
6
+ * @param ms The minimum time interval between function calls, in milliseconds.
7
+ * @returns A debounced version of the function.
8
+ */
9
+ export declare function debounce<T extends (...args: any[]) => void>(
10
+ fn: T,
11
+ ms: number
12
+ ): (...args: Parameters<T>) => void;
13
+
14
+ export default debounce;
@@ -0,0 +1,16 @@
1
+ // @ts-self-types="./debounce.d.ts"
2
+
3
+ 'use strict';
4
+
5
+ export const debounce = (fn, ms) => {
6
+ let handle = null;
7
+ return (...args) => {
8
+ if (handle) clearTimeout(handle);
9
+ handle = setTimeout(() => {
10
+ handle = null;
11
+ fn(...args);
12
+ }, ms);
13
+ };
14
+ };
15
+
16
+ export default debounce;