time-queues 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +4 -0
- package/llms-full.txt +19 -9
- package/llms.txt +9 -5
- package/package.json +23 -11
- package/src/Counter.d.ts +16 -2
- package/src/Counter.js +12 -13
- package/src/FrameQueue.d.ts +10 -3
- package/src/FrameQueue.js +1 -17
- package/src/IdleQueue.d.ts +16 -6
- package/src/IdleQueue.js +1 -17
- package/src/LimitedQueue.d.ts +3 -3
- package/src/LimitedQueue.js +11 -12
- package/src/ListQueue.d.ts +19 -5
- package/src/ListQueue.js +23 -18
- package/src/MicroTask.d.ts +27 -6
- package/src/MicroTask.js +22 -17
- package/src/MicroTaskQueue.d.ts +3 -3
- package/src/MicroTaskQueue.js +9 -27
- package/src/PageWatcher.d.ts +2 -2
- package/src/PageWatcher.js +14 -9
- package/src/Retainer.d.ts +4 -2
- package/src/Retainer.js +25 -13
- package/src/Scheduler.d.ts +44 -42
- package/src/Scheduler.js +21 -3
- package/src/Throttler.d.ts +23 -10
- package/src/Throttler.js +9 -8
- package/src/audit.js +1 -1
- package/src/defer.js +1 -0
- package/src/index.d.ts +23 -25
- package/src/index.js +29 -0
- package/src/random-dist.d.ts +1 -1
- package/src/random-dist.js +9 -6
- package/src/random-sleep.d.ts +4 -4
- package/src/sample.js +1 -1
- package/src/when-dom-loaded.js +2 -1
- package/src/when-loaded.js +2 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -140,6 +140,8 @@ BSD-3-Clause
|
|
|
140
140
|
|
|
141
141
|
## Release History
|
|
142
142
|
|
|
143
|
+
- 1.4.0 _Multiple fixes that improve edge cases, minor additive API changes for edge cases._
|
|
144
|
+
- 1.3.2 _Bug fixes (Scheduler, LimitedQueue, PageWatcher, Retainer), corrected `.d.ts` declarations, expanded tests, documentation fixes._
|
|
143
145
|
- 1.3.1 _Fixed `.d.ts` declarations, consolidated TS typing tests, improved documentation._
|
|
144
146
|
- 1.3.0 _Added `batch()`, `LimitedQueue`, random distributions and random sleep functions._
|
|
145
147
|
- 1.2.4 _Updated dependencies._
|
|
@@ -156,3 +158,5 @@ BSD-3-Clause
|
|
|
156
158
|
- 1.0.2 _Updated deps (`list-toolkit`)._
|
|
157
159
|
- 1.0.1 _Minor update in README._
|
|
158
160
|
- 1.0.0 _Initial release._
|
|
161
|
+
|
|
162
|
+
See [release notes](wiki/Release-notes) for more details and history.
|
package/llms-full.txt
CHANGED
|
@@ -20,6 +20,10 @@ npm install time-queues
|
|
|
20
20
|
## Quick start
|
|
21
21
|
|
|
22
22
|
```js
|
|
23
|
+
// Bare imports work as a barrel:
|
|
24
|
+
import {sleep, Scheduler, repeat, batch} from 'time-queues';
|
|
25
|
+
|
|
26
|
+
// Or subpath imports for tree-shaking-friendly use:
|
|
23
27
|
import sleep from 'time-queues/sleep.js';
|
|
24
28
|
import {Scheduler, repeat} from 'time-queues/Scheduler.js';
|
|
25
29
|
import {batch} from 'time-queues/batch.js';
|
|
@@ -54,7 +58,7 @@ MicroTaskQueue (abstract queue base)
|
|
|
54
58
|
Standalone: Counter, Throttler, Retainer, CancelTaskError
|
|
55
59
|
```
|
|
56
60
|
|
|
57
|
-
All modules are ESM.
|
|
61
|
+
All modules are ESM. Bare imports (`from 'time-queues'`) and subpath imports (`from 'time-queues/<module>.js'`) are both supported.
|
|
58
62
|
|
|
59
63
|
## API Reference
|
|
60
64
|
|
|
@@ -78,10 +82,12 @@ class MicroTask {
|
|
|
78
82
|
fn: () => unknown;
|
|
79
83
|
isCanceled: boolean;
|
|
80
84
|
get promise(): Promise<unknown> | null;
|
|
85
|
+
get settled(): boolean;
|
|
86
|
+
get cancelError(): Error | null;
|
|
81
87
|
constructor(fn: () => unknown);
|
|
82
|
-
makePromise(): this;
|
|
83
|
-
resolve(value: unknown): this;
|
|
84
|
-
cancel(error?: Error): this;
|
|
88
|
+
makePromise(): this; // idempotent; if already canceled, fresh promise settles immediately as CancelTaskError
|
|
89
|
+
resolve(value: unknown): this; // throws if makePromise() has not been called
|
|
90
|
+
cancel(error?: Error): this; // stores first error reason; replayed on later makePromise() if no promise yet
|
|
85
91
|
}
|
|
86
92
|
export default MicroTask;
|
|
87
93
|
```
|
|
@@ -141,6 +147,9 @@ class Task extends MicroTask {
|
|
|
141
147
|
class Scheduler extends MicroTaskQueue {
|
|
142
148
|
paused: boolean;
|
|
143
149
|
tolerance: number;
|
|
150
|
+
queue: MinHeap<Task>; // pending tasks ordered by `time`
|
|
151
|
+
stopQueue: (() => void) | null;
|
|
152
|
+
onError: ((error: unknown, task: Task) => void) | null; // optional per-task error handler; default null surfaces via unhandled-rejection
|
|
144
153
|
constructor(paused?: boolean, tolerance?: number);
|
|
145
154
|
get isEmpty(): boolean;
|
|
146
155
|
get nextTime(): number;
|
|
@@ -205,8 +214,8 @@ class LimitedQueue extends ListQueue {
|
|
|
205
214
|
get activeTasks(): number;
|
|
206
215
|
get isIdle(): boolean;
|
|
207
216
|
waitForIdle(): Promise<void>;
|
|
208
|
-
enqueue(fn: () => unknown): Task;
|
|
209
|
-
schedule(fn: (() => unknown) | null | undefined): Task;
|
|
217
|
+
enqueue(fn: (arg: {task: Task, queue: LimitedQueue}) => unknown): Task;
|
|
218
|
+
schedule(fn: ((arg: {task: Task, queue: LimitedQueue}) => unknown) | null | undefined): Task;
|
|
210
219
|
}
|
|
211
220
|
export { Task };
|
|
212
221
|
export default LimitedQueue;
|
|
@@ -244,9 +253,10 @@ class Counter {
|
|
|
244
253
|
increment(): void;
|
|
245
254
|
decrement(): void;
|
|
246
255
|
advance(amount?: number): void;
|
|
247
|
-
waitForZero(): Promise<number>;
|
|
256
|
+
waitForZero(): Promise<number>; // resolves with NaN if clearWaiters() called first
|
|
248
257
|
waitFor(fn: (count: number) => boolean): Promise<number>;
|
|
249
258
|
clearWaiters(): void;
|
|
259
|
+
notify(): void; // call after direct `count` mutation
|
|
250
260
|
}
|
|
251
261
|
export default Counter;
|
|
252
262
|
```
|
|
@@ -291,12 +301,12 @@ interface RetainerOptions<T = unknown> {
|
|
|
291
301
|
|
|
292
302
|
class Retainer<T = unknown> implements RetainerOptions<T> {
|
|
293
303
|
counter: number;
|
|
294
|
-
value: T | null;
|
|
304
|
+
readonly value: T | null;
|
|
295
305
|
create: () => Promise<T> | T;
|
|
296
306
|
destroy: (value: T) => Promise<void> | void;
|
|
297
307
|
retentionPeriod: number;
|
|
298
308
|
constructor(options: RetainerOptions<T>);
|
|
299
|
-
async get(): Promise<T>;
|
|
309
|
+
async get(): Promise<T>; // concurrent get() calls share one create()
|
|
300
310
|
async release(immediately?: boolean): Promise<this>;
|
|
301
311
|
}
|
|
302
312
|
export default Retainer;
|
package/llms.txt
CHANGED
|
@@ -16,6 +16,10 @@ npm install time-queues
|
|
|
16
16
|
## Quick start
|
|
17
17
|
|
|
18
18
|
```js
|
|
19
|
+
// Bare imports work as a barrel:
|
|
20
|
+
import {sleep, Scheduler, repeat, batch} from 'time-queues';
|
|
21
|
+
|
|
22
|
+
// Or subpath imports for tree-shaking-friendly use:
|
|
19
23
|
import sleep from 'time-queues/sleep.js';
|
|
20
24
|
import {Scheduler, repeat} from 'time-queues/Scheduler.js';
|
|
21
25
|
import {batch} from 'time-queues/batch.js';
|
|
@@ -40,7 +44,7 @@ const results = await batch(
|
|
|
40
44
|
|
|
41
45
|
### Queues
|
|
42
46
|
|
|
43
|
-
- **Scheduler** (`time-queues/Scheduler.js`) — time-based task scheduling with delays, dates, and repeats. Extends MicroTaskQueue. Uses a min-heap. Exports: `Scheduler`, `Task`, `repeat(fn, delay)`, `scheduler` (global instance).
|
|
47
|
+
- **Scheduler** (`time-queues/Scheduler.js`) — time-based task scheduling with delays, dates, and repeats. Extends MicroTaskQueue. Uses a min-heap. Exports: `Scheduler`, `Task`, `repeat(fn, delay)`, `scheduler` (global instance). Optional `scheduler.onError(error, task)` callback handles per-task exceptions; loop continues regardless.
|
|
44
48
|
- **IdleQueue** (`time-queues/IdleQueue.js`) — run tasks during browser idle periods via `requestIdleCallback()`. Extends ListQueue. Exports: `IdleQueue`, `idleQueue` (global instance), `defer(fn)`.
|
|
45
49
|
- **FrameQueue** (`time-queues/FrameQueue.js`) — run tasks in animation frames via `requestAnimationFrame()`. Extends ListQueue. Exports: `FrameQueue`, `frameQueue` (global instance).
|
|
46
50
|
- **LimitedQueue** (`time-queues/LimitedQueue.js`) — concurrency-controlled async queue. Extends ListQueue. Exports: `LimitedQueue`, `Task`.
|
|
@@ -54,18 +58,18 @@ const results = await batch(
|
|
|
54
58
|
- **debounce** (`time-queues/debounce.js`) — `debounce(fn, ms): fn`. Delay until input stabilizes.
|
|
55
59
|
- **sample** (`time-queues/sample.js`) — `sample(fn, ms): fn`. Execute at regular intervals with last seen args.
|
|
56
60
|
- **audit** (`time-queues/audit.js`) — `audit(fn, ms): fn`. Collect then execute after delay with last seen args.
|
|
57
|
-
- **batch** (`time-queues/batch.js`) — `batch(fns, limit?): Promise<results[]>`. Run async operations with concurrency limit (default:
|
|
61
|
+
- **batch** (`time-queues/batch.js`) — `batch(fns, limit?): Promise<results[]>`. Run async operations with concurrency limit (default: 4).
|
|
58
62
|
|
|
59
63
|
### Supporting classes
|
|
60
64
|
|
|
61
65
|
- **Throttler** (`time-queues/Throttler.js`) — key-based rate limiting with vacuum cleanup. Methods: `getDelay(key)`, `wait(key)`, `startVacuum()`, `stopVacuum()`.
|
|
62
66
|
- **Retainer** (`time-queues/Retainer.js`) — resource lifecycle management with reference counting. Methods: `get(): Promise<value>`, `release(): Promise<void>`.
|
|
63
|
-
- **Counter** (`time-queues/Counter.js`) — numeric counter with async waiting. Methods: `increment()`, `decrement()`, `advance(delta)`, `waitForZero()`, `waitFor(
|
|
67
|
+
- **Counter** (`time-queues/Counter.js`) — numeric counter with async waiting. Methods: `increment()`, `decrement()`, `advance(delta)`, `waitForZero()`, `waitFor(predicate)`, `clearWaiters()` (resolves pending with `NaN`), `notify()` (call after direct `count` mutation).
|
|
64
68
|
- **CancelTaskError** (`time-queues/CancelTaskError.js`) — `Error` subclass for task cancellation signals.
|
|
65
69
|
|
|
66
70
|
### Base classes
|
|
67
71
|
|
|
68
|
-
- **MicroTask** (`time-queues/MicroTask.js`) — base task unit with lazy promise creation. Methods: `makePromise()`, `resolve(value)
|
|
72
|
+
- **MicroTask** (`time-queues/MicroTask.js`) — base task unit with lazy promise creation. Methods: `makePromise()`, `resolve(value)` (throws if no promise yet), `cancel(error)` (stores `error`, replays as `cause` on later `makePromise()`). Getters: `promise`, `settled`, `cancelError`.
|
|
69
73
|
- **MicroTaskQueue** (`time-queues/MicroTaskQueue.js`) — abstract queue base class. Methods: `enqueue(fn)`, `dequeue(task)`, `schedule(fn)`, `clear()`, `pause()`, `resume()`.
|
|
70
74
|
- **ListQueue** (`time-queues/ListQueue.js`) — linked-list queue implementation extending MicroTaskQueue. Adds `startQueue()` lifecycle.
|
|
71
75
|
|
|
@@ -81,7 +85,7 @@ const results = await batch(
|
|
|
81
85
|
|
|
82
86
|
## Key concepts
|
|
83
87
|
|
|
84
|
-
- All modules are **ESM
|
|
88
|
+
- All modules are **ESM**. Bare imports (`from 'time-queues'`) and subpath imports (`from 'time-queues/<module>.js'`) both work.
|
|
85
89
|
- **Inheritance**: `MicroTaskQueue` → `ListQueue` → `IdleQueue`/`FrameQueue`/`LimitedQueue`/`PageWatcher`. `Scheduler` extends `MicroTaskQueue` directly.
|
|
86
90
|
- **Lazy promises** — only created when `.makePromise()` is called or via `schedule()`.
|
|
87
91
|
- **Clean cancellation** — all tasks support cancellation via `CancelTaskError`.
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "time-queues",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Lightweight async task scheduling and concurrency control: schedulers, idle/frame/limited queues, throttle, debounce, batch, page lifecycle, random delays. Browsers, Node.js, Deno, Bun.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
8
9
|
"./*": "./src/*"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
"test:seq:bun": "bun run `tape6-seq --self` --flags FO",
|
|
19
20
|
"test:seq:deno": "deno run -A `tape6-seq --self` --flags FO",
|
|
20
21
|
"ts-check": "tsc --noEmit",
|
|
22
|
+
"js-check": "tsc --project tsconfig.check.json",
|
|
21
23
|
"ts-test": "tape6 --flags FO '/ts-tests/test-*.*ts'",
|
|
22
24
|
"ts-test:bun": "tape6-bun --flags FO '/ts-tests/test-*.*ts'",
|
|
23
25
|
"ts-test:deno": "tape6-deno --flags FO '/ts-tests/test-*.*ts'",
|
|
@@ -33,7 +35,7 @@
|
|
|
33
35
|
},
|
|
34
36
|
"repository": {
|
|
35
37
|
"type": "git",
|
|
36
|
-
"url": "git+
|
|
38
|
+
"url": "git+https://github.com/uhop/time-queues.git"
|
|
37
39
|
},
|
|
38
40
|
"keywords": [
|
|
39
41
|
"scheduler",
|
|
@@ -41,17 +43,20 @@
|
|
|
41
43
|
"async-queue",
|
|
42
44
|
"concurrency",
|
|
43
45
|
"throttle",
|
|
46
|
+
"throttler",
|
|
44
47
|
"debounce",
|
|
45
48
|
"rate-limit",
|
|
46
49
|
"batch",
|
|
47
50
|
"sleep",
|
|
48
51
|
"defer",
|
|
49
52
|
"idle-queue",
|
|
53
|
+
"frame-queue",
|
|
54
|
+
"limited-queue",
|
|
50
55
|
"requestIdleCallback",
|
|
51
56
|
"requestAnimationFrame",
|
|
52
57
|
"page-lifecycle",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
58
|
+
"microtask",
|
|
59
|
+
"retainer",
|
|
55
60
|
"queue",
|
|
56
61
|
"esm",
|
|
57
62
|
"typescript"
|
|
@@ -59,6 +64,9 @@
|
|
|
59
64
|
"author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (https://www.lazutkin.com/)",
|
|
60
65
|
"funding": "https://github.com/sponsors/uhop",
|
|
61
66
|
"license": "BSD-3-Clause",
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=22"
|
|
69
|
+
},
|
|
62
70
|
"bugs": {
|
|
63
71
|
"url": "https://github.com/uhop/time-queues/issues"
|
|
64
72
|
},
|
|
@@ -75,7 +83,11 @@
|
|
|
75
83
|
],
|
|
76
84
|
"tape6": {
|
|
77
85
|
"tests": [
|
|
78
|
-
"/tests/test
|
|
86
|
+
"/tests/test-*.js",
|
|
87
|
+
"/tests/test-*.mjs"
|
|
88
|
+
],
|
|
89
|
+
"cli": [
|
|
90
|
+
"/tests/test-*.cjs"
|
|
79
91
|
],
|
|
80
92
|
"importmap": {
|
|
81
93
|
"imports": {
|
|
@@ -86,13 +98,13 @@
|
|
|
86
98
|
}
|
|
87
99
|
},
|
|
88
100
|
"devDependencies": {
|
|
89
|
-
"@types/node": "^25.
|
|
90
|
-
"prettier": "^3.8.
|
|
91
|
-
"tape-six": "^1.
|
|
92
|
-
"tape-six-proc": "^1.2.
|
|
93
|
-
"typescript": "^
|
|
101
|
+
"@types/node": "^25.6.1",
|
|
102
|
+
"prettier": "^3.8.3",
|
|
103
|
+
"tape-six": "^1.9.0",
|
|
104
|
+
"tape-six-proc": "^1.2.9",
|
|
105
|
+
"typescript": "^6.0.3"
|
|
94
106
|
},
|
|
95
107
|
"dependencies": {
|
|
96
|
-
"list-toolkit": "^2.3.
|
|
108
|
+
"list-toolkit": "^2.3.2"
|
|
97
109
|
}
|
|
98
110
|
}
|
package/src/Counter.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare class Counter {
|
|
|
22
22
|
* Sets the counter to a specific value.
|
|
23
23
|
* @param value The new value for the counter.
|
|
24
24
|
*/
|
|
25
|
-
set value(value: number)
|
|
25
|
+
set value(value: number);
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Increments the counter.
|
|
@@ -42,21 +42,35 @@ export declare class Counter {
|
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Waits for the counter to reach zero. If the counter is already zero, the promise is resolved immediately.
|
|
45
|
+
* Resolves with `0` on a normal wait, or with `NaN` if `clearWaiters()` is called before the counter reaches zero.
|
|
45
46
|
* @returns A promise that resolves when the counter reaches zero.
|
|
46
47
|
*/
|
|
47
48
|
waitForZero(): Promise<number>;
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
51
|
* Waits for the counter to reach a specific value. If the counter is already at the desired value, the promise is resolved immediately.
|
|
52
|
+
* Resolves with the matching count, or with `NaN` if `clearWaiters()` is called before the predicate matches.
|
|
51
53
|
* @param fn A function that returns `true` when the counter reaches the desired value.
|
|
52
54
|
* @returns A promise that resolves when the counter reaches the desired value.
|
|
53
55
|
*/
|
|
54
56
|
waitFor(fn: (count: number) => boolean): Promise<number>;
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
|
-
* Clears all waiters
|
|
59
|
+
* Clears all pending waiters by resolving them with `NaN`.
|
|
60
|
+
* Use `Number.isNaN(value)` to distinguish "queue cleared" from a real count of zero.
|
|
61
|
+
* Most consumers `await` the result without inspecting it; checking is only needed
|
|
62
|
+
* when you both clear waiters during teardown and have downstream arithmetic that
|
|
63
|
+
* shouldn't be NaN-poisoned.
|
|
58
64
|
*/
|
|
59
65
|
clearWaiters(): void;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Re-evaluates pending waiters against the current `count` and resolves any
|
|
69
|
+
* whose predicate now matches. Called automatically by `value`, `increment`,
|
|
70
|
+
* `decrement`, and `advance`. Call manually only when mutating `count`
|
|
71
|
+
* directly (which bypasses the setter).
|
|
72
|
+
*/
|
|
73
|
+
notify(): void;
|
|
60
74
|
}
|
|
61
75
|
|
|
62
76
|
export default Counter;
|
package/src/Counter.js
CHANGED
|
@@ -4,15 +4,15 @@ export class Counter {
|
|
|
4
4
|
constructor(initial = 0) {
|
|
5
5
|
this.count = initial;
|
|
6
6
|
this.zeroWaiters = [];
|
|
7
|
-
this.functionWaiters =
|
|
7
|
+
this.functionWaiters = [];
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
get value() {
|
|
11
11
|
return this.count;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
set value(
|
|
15
|
-
this.count =
|
|
14
|
+
set value(newValue) {
|
|
15
|
+
this.count = newValue;
|
|
16
16
|
this.notify();
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -38,7 +38,7 @@ export class Counter {
|
|
|
38
38
|
|
|
39
39
|
waitFor(fn) {
|
|
40
40
|
if (fn(this.count)) return Promise.resolve(this.count);
|
|
41
|
-
return new Promise(resolve => this.functionWaiters.
|
|
41
|
+
return new Promise(resolve => this.functionWaiters.push({fn, resolve}));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
clearWaiters() {
|
|
@@ -49,9 +49,9 @@ export class Counter {
|
|
|
49
49
|
resolve(NaN);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
if (this.functionWaiters.
|
|
52
|
+
if (this.functionWaiters.length > 0) {
|
|
53
53
|
const functionWaiters = this.functionWaiters;
|
|
54
|
-
this.functionWaiters =
|
|
54
|
+
this.functionWaiters = [];
|
|
55
55
|
for (const {resolve} of functionWaiters) {
|
|
56
56
|
resolve(NaN);
|
|
57
57
|
}
|
|
@@ -66,17 +66,16 @@ export class Counter {
|
|
|
66
66
|
resolve(0);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
-
if (this.functionWaiters.
|
|
70
|
-
const
|
|
69
|
+
if (this.functionWaiters.length > 0) {
|
|
70
|
+
const remaining = [];
|
|
71
71
|
for (const waiter of this.functionWaiters) {
|
|
72
|
-
if (waiter.fn(this.count))
|
|
73
|
-
}
|
|
74
|
-
if (ready.length > 0) {
|
|
75
|
-
for (const waiter of ready) {
|
|
72
|
+
if (waiter.fn(this.count)) {
|
|
76
73
|
waiter.resolve(this.count);
|
|
77
|
-
|
|
74
|
+
} else {
|
|
75
|
+
remaining.push(waiter);
|
|
78
76
|
}
|
|
79
77
|
}
|
|
78
|
+
this.functionWaiters = remaining;
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
}
|
package/src/FrameQueue.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {ListQueue, Task} from './ListQueue';
|
|
1
|
+
import {ListQueue, Task} from './ListQueue.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A queue based on [requestAnimationFrame()](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame).
|
|
@@ -33,7 +33,7 @@ export declare class FrameQueue extends ListQueue {
|
|
|
33
33
|
* @param fn The function to execute.
|
|
34
34
|
* @returns The task object.
|
|
35
35
|
*/
|
|
36
|
-
enqueue(fn: ({timeStamp: number
|
|
36
|
+
enqueue(fn: (arg: {timeStamp: number; task: Task; queue: FrameQueue}) => unknown): Task;
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Dequeues a task.
|
|
@@ -48,7 +48,7 @@ export declare class FrameQueue extends ListQueue {
|
|
|
48
48
|
* @returns The task object.
|
|
49
49
|
*/
|
|
50
50
|
schedule(
|
|
51
|
-
fn: (({timeStamp: number
|
|
51
|
+
fn: ((arg: {timeStamp: number; task: Task; queue: FrameQueue}) => unknown) | null | undefined
|
|
52
52
|
): Task;
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -75,6 +75,13 @@ export declare class FrameQueue extends ListQueue {
|
|
|
75
75
|
* @returns The function that stops the queue.
|
|
76
76
|
*/
|
|
77
77
|
startQueue(): (() => void) | null;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Processes pending tasks. Called by `requestAnimationFrame()` — not part of
|
|
81
|
+
* the typical user surface; documented for subclasses that need to override.
|
|
82
|
+
* @param timeStamp The high-resolution timestamp from the rAF callback.
|
|
83
|
+
*/
|
|
84
|
+
processTasks(timeStamp: number): void;
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
/**
|
package/src/FrameQueue.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// @ts-self-types="./FrameQueue.d.ts"
|
|
2
2
|
|
|
3
|
-
import List from 'list-toolkit/list.js';
|
|
4
3
|
import ListQueue from './ListQueue.js';
|
|
5
4
|
|
|
6
5
|
export class FrameQueue extends ListQueue {
|
|
@@ -19,22 +18,7 @@ export class FrameQueue extends ListQueue {
|
|
|
19
18
|
this.stopQueue();
|
|
20
19
|
this.stopQueue = null;
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
if (!isNaN(this.batch)) {
|
|
24
|
-
const start = Date.now();
|
|
25
|
-
while (Date.now() - start < this.batch && !this.list.isEmpty) {
|
|
26
|
-
const task = this.list.popFront();
|
|
27
|
-
task.fn({timeStamp, task, queue: this});
|
|
28
|
-
}
|
|
29
|
-
} else {
|
|
30
|
-
const list = this.list;
|
|
31
|
-
this.list = new List();
|
|
32
|
-
while (!list.isEmpty) {
|
|
33
|
-
const task = list.popFront();
|
|
34
|
-
task.fn({timeStamp, task, queue: this});
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
21
|
+
this._drainBatch(this.batch, {timeStamp});
|
|
38
22
|
if (!this.list.isEmpty) this.stopQueue = this.startQueue();
|
|
39
23
|
}
|
|
40
24
|
}
|
package/src/IdleQueue.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {ListQueue, Task} from './ListQueue';
|
|
1
|
+
import {ListQueue, Task} from './ListQueue.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A queue based on [requestIdleCallback()](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback).
|
|
@@ -20,7 +20,7 @@ export declare class IdleQueue extends ListQueue {
|
|
|
20
20
|
/**
|
|
21
21
|
* The options passed to `requestIdleCallback()`.
|
|
22
22
|
*/
|
|
23
|
-
options:
|
|
23
|
+
options: IdleRequestOptions | undefined;
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Creates a new idle queue.
|
|
@@ -28,7 +28,7 @@ export declare class IdleQueue extends ListQueue {
|
|
|
28
28
|
* @param timeoutBatchInMs The timeout batch size in milliseconds.
|
|
29
29
|
* @param options The options passed to `requestIdleCallback()`.
|
|
30
30
|
*/
|
|
31
|
-
constructor(paused?: boolean, timeoutBatchInMs?: number, options?:
|
|
31
|
+
constructor(paused?: boolean, timeoutBatchInMs?: number, options?: IdleRequestOptions);
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Whether the queue is empty.
|
|
@@ -40,7 +40,7 @@ export declare class IdleQueue extends ListQueue {
|
|
|
40
40
|
* @param fn The function to execute.
|
|
41
41
|
* @returns The task object.
|
|
42
42
|
*/
|
|
43
|
-
enqueue(fn: ({deadline: IdleDeadline
|
|
43
|
+
enqueue(fn: (arg: {deadline: IdleDeadline; task: Task; queue: IdleQueue}) => unknown): Task;
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Dequeues a task.
|
|
@@ -55,7 +55,10 @@ export declare class IdleQueue extends ListQueue {
|
|
|
55
55
|
* @returns The task object.
|
|
56
56
|
*/
|
|
57
57
|
schedule(
|
|
58
|
-
fn:
|
|
58
|
+
fn:
|
|
59
|
+
| ((arg: {deadline: IdleDeadline; task: Task; queue: IdleQueue}) => unknown)
|
|
60
|
+
| null
|
|
61
|
+
| undefined
|
|
59
62
|
): Task;
|
|
60
63
|
|
|
61
64
|
/**
|
|
@@ -82,6 +85,13 @@ export declare class IdleQueue extends ListQueue {
|
|
|
82
85
|
* @returns The function that stops the queue.
|
|
83
86
|
*/
|
|
84
87
|
startQueue(): (() => void) | null;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Processes pending tasks. Called by `requestIdleCallback()` — not part of
|
|
91
|
+
* the typical user surface; documented for subclasses that need to override.
|
|
92
|
+
* @param deadline The `IdleDeadline` from the rIC callback.
|
|
93
|
+
*/
|
|
94
|
+
processTasks(deadline: IdleDeadline): void;
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
/**
|
|
@@ -93,7 +103,7 @@ export const idleQueue: IdleQueue;
|
|
|
93
103
|
* A function that schedules a task to run in the next idle period.
|
|
94
104
|
*/
|
|
95
105
|
export const defer: (
|
|
96
|
-
fn: ({deadline: IdleDeadline
|
|
106
|
+
fn: (arg: {deadline: IdleDeadline; task: Task; queue: IdleQueue}) => unknown
|
|
97
107
|
) => Task;
|
|
98
108
|
|
|
99
109
|
export default idleQueue;
|
package/src/IdleQueue.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// @ts-self-types="./IdleQueue.d.ts"
|
|
2
2
|
|
|
3
|
-
import List from 'list-toolkit/list.js';
|
|
4
3
|
import ListQueue from './ListQueue.js';
|
|
5
4
|
|
|
6
5
|
// Based on information from https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API
|
|
@@ -22,29 +21,14 @@ export class IdleQueue extends ListQueue {
|
|
|
22
21
|
this.stopQueue();
|
|
23
22
|
this.stopQueue = null;
|
|
24
23
|
}
|
|
25
|
-
|
|
26
24
|
if (deadline.didTimeout) {
|
|
27
|
-
|
|
28
|
-
const start = Date.now();
|
|
29
|
-
while (Date.now() - start < this.timeoutBatch && !this.list.isEmpty) {
|
|
30
|
-
const task = this.list.popFront();
|
|
31
|
-
task.fn({deadline, task, queue: this});
|
|
32
|
-
}
|
|
33
|
-
} else {
|
|
34
|
-
const list = this.list;
|
|
35
|
-
this.list = new List();
|
|
36
|
-
while (!list.isEmpty) {
|
|
37
|
-
const task = list.popFront();
|
|
38
|
-
task.fn({deadline, task, queue: this});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
25
|
+
this._drainBatch(this.timeoutBatch, {deadline});
|
|
41
26
|
} else {
|
|
42
27
|
while (deadline.timeRemaining() > 0 && !this.list.isEmpty) {
|
|
43
28
|
const task = this.list.popFront();
|
|
44
29
|
task.fn({deadline, task, queue: this});
|
|
45
30
|
}
|
|
46
31
|
}
|
|
47
|
-
|
|
48
32
|
if (!this.list.isEmpty) this.stopQueue = this.startQueue();
|
|
49
33
|
}
|
|
50
34
|
}
|
package/src/LimitedQueue.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {ListQueue, Task} from './ListQueue';
|
|
1
|
+
import {ListQueue, Task} from './ListQueue.js';
|
|
2
2
|
|
|
3
3
|
export {Task};
|
|
4
4
|
|
|
@@ -58,7 +58,7 @@ export declare class LimitedQueue extends ListQueue {
|
|
|
58
58
|
* @param fn The function to execute when the microtask is scheduled.
|
|
59
59
|
* @returns The enqueued microtask.
|
|
60
60
|
*/
|
|
61
|
-
enqueue(fn: () => unknown): Task;
|
|
61
|
+
enqueue(fn: (arg: {task: Task; queue: LimitedQueue}) => unknown): Task;
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Dequeues a microtask.
|
|
@@ -72,7 +72,7 @@ export declare class LimitedQueue extends ListQueue {
|
|
|
72
72
|
* @param fn The function to execute. If `undefined` or `null`, the task's promise will be resolved with the function's arguments. Otherwise, it is resolved with the function's return value.
|
|
73
73
|
* @returns The task object.
|
|
74
74
|
*/
|
|
75
|
-
schedule(fn: (() => unknown) | null | undefined): Task;
|
|
75
|
+
schedule(fn: ((arg: {task: Task; queue: LimitedQueue}) => unknown) | null | undefined): Task;
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
78
|
* Clears the queue.
|
package/src/LimitedQueue.js
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
import ListQueue from './ListQueue.js';
|
|
4
4
|
|
|
5
|
+
const wrap = fn =>
|
|
6
|
+
new Promise((resolve, reject) => {
|
|
7
|
+
try {
|
|
8
|
+
resolve(fn());
|
|
9
|
+
} catch (error) {
|
|
10
|
+
reject(error);
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
5
14
|
export class LimitedQueue extends ListQueue {
|
|
6
15
|
#taskLimit;
|
|
7
16
|
#activeTasks;
|
|
@@ -9,7 +18,7 @@ export class LimitedQueue extends ListQueue {
|
|
|
9
18
|
|
|
10
19
|
constructor(limit, paused) {
|
|
11
20
|
super(paused);
|
|
12
|
-
this.#taskLimit = limit;
|
|
21
|
+
this.#taskLimit = Math.max(1, limit);
|
|
13
22
|
this.#activeTasks = 0;
|
|
14
23
|
this.#idleWaiters = [];
|
|
15
24
|
}
|
|
@@ -57,22 +66,12 @@ export class LimitedQueue extends ListQueue {
|
|
|
57
66
|
while (this.#activeTasks < this.#taskLimit && !this.list.isEmpty) {
|
|
58
67
|
const task = this.list.popFront();
|
|
59
68
|
++this.#activeTasks;
|
|
60
|
-
|
|
69
|
+
wrap(() => task.fn({task, queue: this})).finally(() => {
|
|
61
70
|
--this.#activeTasks;
|
|
62
71
|
this.#processTasks();
|
|
63
72
|
});
|
|
64
73
|
}
|
|
65
74
|
}
|
|
66
|
-
|
|
67
|
-
static wrap(fn) {
|
|
68
|
-
return new Promise((resolve, reject) => {
|
|
69
|
-
try {
|
|
70
|
-
resolve(fn());
|
|
71
|
-
} catch (error) {
|
|
72
|
-
reject(error);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
export default LimitedQueue;
|
package/src/ListQueue.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import List from 'list-toolkit/list.js';
|
|
2
|
+
import {MicroTask} from './MicroTask.js';
|
|
3
|
+
import {MicroTaskQueue} from './MicroTaskQueue.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A list-based queue of microtasks that will be executed when scheduled.
|
|
@@ -12,6 +12,11 @@ export declare class ListQueue extends MicroTaskQueue {
|
|
|
12
12
|
*/
|
|
13
13
|
paused: boolean;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The linked list of pending microtasks.
|
|
17
|
+
*/
|
|
18
|
+
list: List<MicroTask>;
|
|
19
|
+
|
|
15
20
|
/**
|
|
16
21
|
* The function that stops the queue.
|
|
17
22
|
* It is used internally by `pause()` and `resume()`.
|
|
@@ -34,7 +39,7 @@ export declare class ListQueue extends MicroTaskQueue {
|
|
|
34
39
|
* @param fn The function to execute when the microtask is scheduled.
|
|
35
40
|
* @returns The enqueued microtask.
|
|
36
41
|
*/
|
|
37
|
-
enqueue(fn: () => unknown): MicroTask;
|
|
42
|
+
enqueue(fn: (...args: any[]) => unknown): MicroTask;
|
|
38
43
|
|
|
39
44
|
/**
|
|
40
45
|
* Dequeues a microtask.
|
|
@@ -48,7 +53,7 @@ export declare class ListQueue extends MicroTaskQueue {
|
|
|
48
53
|
* @param fn The function to execute. If `undefined` or `null`, the task's promise will be resolved with the function's arguments. Otherwise, it is resolved with the function's return value.
|
|
49
54
|
* @returns The task object.
|
|
50
55
|
*/
|
|
51
|
-
schedule(fn: (() => unknown) | null | undefined): MicroTask;
|
|
56
|
+
schedule(fn: ((...args: any[]) => unknown) | null | undefined): MicroTask;
|
|
52
57
|
|
|
53
58
|
/**
|
|
54
59
|
* Clears the queue.
|
|
@@ -75,6 +80,15 @@ export declare class ListQueue extends MicroTaskQueue {
|
|
|
75
80
|
* @returns The function that stops the queue.
|
|
76
81
|
*/
|
|
77
82
|
startQueue(): (() => void) | null;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Internal helper used by subclasses (FrameQueue, IdleQueue) to drain pending
|
|
86
|
+
* tasks. If `batchMs` is finite, runs tasks until that many milliseconds have
|
|
87
|
+
* elapsed; otherwise swaps in a fresh list and drains the captured one
|
|
88
|
+
* entirely.
|
|
89
|
+
* @internal
|
|
90
|
+
*/
|
|
91
|
+
_drainBatch(batchMs: number, taskContext: object): void;
|
|
78
92
|
}
|
|
79
93
|
|
|
80
94
|
/**
|