time-queues 1.2.4 → 1.3.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.
- package/README.md +117 -36
- package/llms-full.txt +430 -0
- package/llms.txt +95 -0
- package/package.json +43 -12
- package/src/CancelTaskError.d.ts +2 -1
- package/src/CancelTaskError.js +2 -4
- package/src/Counter.d.ts +0 -1
- package/src/Counter.js +0 -2
- package/src/FrameQueue.d.ts +2 -2
- package/src/FrameQueue.js +0 -2
- package/src/IdleQueue.d.ts +2 -2
- package/src/IdleQueue.js +0 -2
- package/src/LimitedQueue.d.ts +104 -0
- package/src/LimitedQueue.js +78 -0
- package/src/ListQueue.d.ts +1 -6
- package/src/ListQueue.js +18 -3
- package/src/MicroTask.d.ts +7 -1
- package/src/MicroTask.js +47 -15
- package/src/MicroTaskQueue.d.ts +24 -20
- package/src/MicroTaskQueue.js +22 -7
- package/src/PageWatcher.d.ts +2 -2
- package/src/PageWatcher.js +0 -2
- package/src/Retainer.d.ts +4 -4
- package/src/Retainer.js +0 -2
- package/src/Scheduler.d.ts +23 -17
- package/src/Scheduler.js +1 -2
- package/src/Throttler.d.ts +7 -2
- package/src/Throttler.js +1 -2
- package/src/audit.js +0 -2
- package/src/batch.d.ts +16 -0
- package/src/batch.js +40 -0
- package/src/debounce.js +0 -2
- package/src/defer.d.ts +3 -4
- package/src/defer.js +0 -2
- package/src/index.d.ts +7 -2
- package/src/random-dist.d.ts +31 -0
- package/src/random-dist.js +27 -0
- package/src/random-sleep.d.ts +57 -0
- package/src/random-sleep.js +27 -0
- package/src/sample.d.ts +1 -1
- package/src/sample.js +0 -2
- package/src/sleep.d.ts +1 -1
- package/src/sleep.js +0 -2
- package/src/throttle.d.ts +2 -1
- package/src/throttle.js +0 -2
- package/src/when-dom-loaded.d.ts +4 -8
- package/src/when-dom-loaded.js +3 -9
- package/src/when-loaded.d.ts +4 -8
- package/src/when-loaded.js +3 -9
package/llms.txt
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# time-queues
|
|
2
|
+
|
|
3
|
+
> Lightweight async task scheduling and concurrency control for JavaScript: schedulers, idle/frame/limited queues, throttle, debounce, batch, page lifecycle, random delays.
|
|
4
|
+
|
|
5
|
+
- NPM: https://npmjs.org/package/time-queues
|
|
6
|
+
- GitHub: https://github.com/uhop/time-queues
|
|
7
|
+
- Wiki: https://github.com/uhop/time-queues/wiki
|
|
8
|
+
- License: BSD-3-Clause
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install time-queues
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import sleep from 'time-queues/sleep.js';
|
|
20
|
+
import {Scheduler, repeat} from 'time-queues/Scheduler.js';
|
|
21
|
+
import {batch} from 'time-queues/batch.js';
|
|
22
|
+
|
|
23
|
+
// Simple delay
|
|
24
|
+
await sleep(1000);
|
|
25
|
+
|
|
26
|
+
// Run a task every 5 seconds
|
|
27
|
+
const scheduler = new Scheduler();
|
|
28
|
+
scheduler.enqueue(repeat(({task, scheduler}) => {
|
|
29
|
+
console.log('tick');
|
|
30
|
+
}, 5000), 5000);
|
|
31
|
+
|
|
32
|
+
// Fetch URLs, max 3 at a time
|
|
33
|
+
const results = await batch(
|
|
34
|
+
urls.map(url => () => fetch(url).then(r => r.json())),
|
|
35
|
+
3
|
|
36
|
+
);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Modules
|
|
40
|
+
|
|
41
|
+
### Queues
|
|
42
|
+
|
|
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).
|
|
44
|
+
- **IdleQueue** (`time-queues/IdleQueue.js`) — run tasks during browser idle periods via `requestIdleCallback()`. Extends ListQueue. Exports: `IdleQueue`, `idleQueue` (global instance), `defer(fn)`.
|
|
45
|
+
- **FrameQueue** (`time-queues/FrameQueue.js`) — run tasks in animation frames via `requestAnimationFrame()`. Extends ListQueue. Exports: `FrameQueue`, `frameQueue` (global instance).
|
|
46
|
+
- **LimitedQueue** (`time-queues/LimitedQueue.js`) — concurrency-controlled async queue. Extends ListQueue. Exports: `LimitedQueue`, `Task`.
|
|
47
|
+
- **PageWatcher** (`time-queues/PageWatcher.js`) — react to page lifecycle changes (active, passive, hidden, frozen, terminated). Extends ListQueue. Exports: `PageWatcher`, `watchStates(states, fn)`, `pageWatcher` (global instance).
|
|
48
|
+
|
|
49
|
+
### Utility functions
|
|
50
|
+
|
|
51
|
+
- **sleep** (`time-queues/sleep.js`) — `sleep(ms | Date): Promise<void>`. Promise-based delay.
|
|
52
|
+
- **defer** (`time-queues/defer.js`) — `defer(fn): void`. Execute on next tick via `requestIdleCallback`/`setImmediate`/`setTimeout`. Also exports `scheduleDefer(fn): Promise`.
|
|
53
|
+
- **throttle** (`time-queues/throttle.js`) — `throttle(fn, ms): fn`. Rate-limit: execute immediately, ignore calls until timeout expires.
|
|
54
|
+
- **debounce** (`time-queues/debounce.js`) — `debounce(fn, ms): fn`. Delay until input stabilizes.
|
|
55
|
+
- **sample** (`time-queues/sample.js`) — `sample(fn, ms): fn`. Execute at regular intervals with last seen args.
|
|
56
|
+
- **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: 5).
|
|
58
|
+
|
|
59
|
+
### Supporting classes
|
|
60
|
+
|
|
61
|
+
- **Throttler** (`time-queues/Throttler.js`) — key-based rate limiting with vacuum cleanup. Methods: `getDelay(key)`, `wait(key)`, `startVacuum()`, `stopVacuum()`.
|
|
62
|
+
- **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(target)`.
|
|
64
|
+
- **CancelTaskError** (`time-queues/CancelTaskError.js`) — `Error` subclass for task cancellation signals.
|
|
65
|
+
|
|
66
|
+
### Base classes
|
|
67
|
+
|
|
68
|
+
- **MicroTask** (`time-queues/MicroTask.js`) — base task unit with lazy promise creation. Methods: `makePromise()`, `resolve(value)`, `cancel(error)`.
|
|
69
|
+
- **MicroTaskQueue** (`time-queues/MicroTaskQueue.js`) — abstract queue base class. Methods: `enqueue(fn)`, `dequeue(task)`, `schedule(fn)`, `clear()`, `pause()`, `resume()`.
|
|
70
|
+
- **ListQueue** (`time-queues/ListQueue.js`) — linked-list queue implementation extending MicroTaskQueue. Adds `startQueue()` lifecycle.
|
|
71
|
+
|
|
72
|
+
### Random utilities
|
|
73
|
+
|
|
74
|
+
- **random-dist** (`time-queues/random-dist.js`) — `uniform(min, max)`, `normal(mean, stdDev, skewness?)`, `expo(lambda)`, `pareto(min, alpha)`.
|
|
75
|
+
- **random-sleep** (`time-queues/random-sleep.js`) — `randomUniformSleep(min, max)`, `randomNormalSleep(mean, stdDev)`, `randomExpoSleep(lambda, scale)`, `randomParetoSleep(min, ratio)`, `randomSleep(max)`. Each returns `() => Promise<void>`.
|
|
76
|
+
|
|
77
|
+
### Page load helpers
|
|
78
|
+
|
|
79
|
+
- **whenDomLoaded** (`time-queues/when-dom-loaded.js`) — `whenDomLoaded(fn)`. Also exports `scheduleWhenDomLoaded(fn): Promise`, `remove(fn): boolean`.
|
|
80
|
+
- **whenLoaded** (`time-queues/when-loaded.js`) — `whenLoaded(fn)`. Also exports `scheduleWhenLoaded(fn): Promise`, `remove(fn): boolean`.
|
|
81
|
+
|
|
82
|
+
## Key concepts
|
|
83
|
+
|
|
84
|
+
- All modules are **ESM** with `import ... from 'time-queues/<module>.js'`.
|
|
85
|
+
- **Inheritance**: `MicroTaskQueue` → `ListQueue` → `IdleQueue`/`FrameQueue`/`LimitedQueue`/`PageWatcher`. `Scheduler` extends `MicroTaskQueue` directly.
|
|
86
|
+
- **Lazy promises** — only created when `.makePromise()` is called or via `schedule()`.
|
|
87
|
+
- **Clean cancellation** — all tasks support cancellation via `CancelTaskError`.
|
|
88
|
+
- **Graceful degradation** — feature detection for browser APIs, works in Node.js/Bun/Deno.
|
|
89
|
+
- **TypeScript** — built-in `.d.ts` declarations for all modules.
|
|
90
|
+
|
|
91
|
+
## Links
|
|
92
|
+
|
|
93
|
+
- Docs: https://github.com/uhop/time-queues/wiki
|
|
94
|
+
- npm: https://www.npmjs.com/package/time-queues
|
|
95
|
+
- Full LLM reference: https://github.com/uhop/time-queues/blob/master/llms-full.txt
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "time-queues",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.3.1",
|
|
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": {
|
|
@@ -12,14 +12,23 @@
|
|
|
12
12
|
"test:bun": "tape6-bun --flags FO",
|
|
13
13
|
"test:deno": "tape6-deno --flags FO",
|
|
14
14
|
"test:proc": "tape6-proc --flags FO",
|
|
15
|
-
"test:proc:bun": "bun run `
|
|
16
|
-
"test:proc:deno": "deno run -A `
|
|
15
|
+
"test:proc:bun": "bun run `tape6-proc --self` --flags FO",
|
|
16
|
+
"test:proc:deno": "deno run -A `tape6-proc --self` --flags FO -r -A",
|
|
17
|
+
"test:seq": "tape6-seq --flags FO",
|
|
18
|
+
"test:seq:bun": "bun run `tape6-seq --self` --flags FO",
|
|
19
|
+
"test:seq:deno": "deno run -A `tape6-seq --self` --flags FO",
|
|
17
20
|
"ts-check": "tsc --noEmit",
|
|
18
21
|
"ts-test": "tape6 --flags FO '/ts-tests/test-*.*ts'",
|
|
19
22
|
"ts-test:bun": "tape6-bun --flags FO '/ts-tests/test-*.*ts'",
|
|
20
23
|
"ts-test:deno": "tape6-deno --flags FO '/ts-tests/test-*.*ts'",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
24
|
+
"ts-test:proc": "tape6-proc --flags FO '/ts-tests/test-*.*ts'",
|
|
25
|
+
"ts-test:proc:bun": "bun run `tape6-proc --self` --flags FO '/ts-tests/test-*.*ts'",
|
|
26
|
+
"ts-test:proc:deno": "deno run -A `tape6-proc --self` --flags FO -r -A '/ts-tests/test-*.*ts'",
|
|
27
|
+
"ts-test:seq": "tape6-seq --flags FO '/ts-tests/test-*.*ts'",
|
|
28
|
+
"ts-test:seq:bun": "bun run `tape6-seq --self` --flags FO '/ts-tests/test-*.*ts'",
|
|
29
|
+
"ts-test:seq:deno": "deno run -A `tape6-seq --self` --flags FO '/ts-tests/test-*.*ts'",
|
|
30
|
+
"lint": "prettier --check .",
|
|
31
|
+
"lint:fix": "prettier --write .",
|
|
23
32
|
"start": "tape6-server --trace"
|
|
24
33
|
},
|
|
25
34
|
"repository": {
|
|
@@ -27,9 +36,25 @@
|
|
|
27
36
|
"url": "git+ssh://git@github.com/uhop/time-queues.git"
|
|
28
37
|
},
|
|
29
38
|
"keywords": [
|
|
39
|
+
"scheduler",
|
|
40
|
+
"task-queue",
|
|
41
|
+
"async-queue",
|
|
42
|
+
"concurrency",
|
|
43
|
+
"throttle",
|
|
44
|
+
"debounce",
|
|
45
|
+
"rate-limit",
|
|
46
|
+
"batch",
|
|
47
|
+
"sleep",
|
|
48
|
+
"defer",
|
|
49
|
+
"idle-queue",
|
|
50
|
+
"requestIdleCallback",
|
|
51
|
+
"requestAnimationFrame",
|
|
52
|
+
"page-lifecycle",
|
|
30
53
|
"timer",
|
|
31
54
|
"time",
|
|
32
|
-
"queue"
|
|
55
|
+
"queue",
|
|
56
|
+
"esm",
|
|
57
|
+
"typescript"
|
|
33
58
|
],
|
|
34
59
|
"author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (https://www.lazutkin.com/)",
|
|
35
60
|
"funding": "https://github.com/sponsors/uhop",
|
|
@@ -37,11 +62,16 @@
|
|
|
37
62
|
"bugs": {
|
|
38
63
|
"url": "https://github.com/uhop/time-queues/issues"
|
|
39
64
|
},
|
|
65
|
+
"github": "https://github.com/uhop/time-queues",
|
|
40
66
|
"homepage": "https://github.com/uhop/time-queues#readme",
|
|
67
|
+
"llms": "https://raw.githubusercontent.com/uhop/time-queues/master/llms.txt",
|
|
68
|
+
"llmsFull": "https://raw.githubusercontent.com/uhop/time-queues/master/llms-full.txt",
|
|
41
69
|
"files": [
|
|
42
70
|
"/src",
|
|
43
71
|
"LICENSE",
|
|
44
|
-
"README.md"
|
|
72
|
+
"README.md",
|
|
73
|
+
"llms.txt",
|
|
74
|
+
"llms-full.txt"
|
|
45
75
|
],
|
|
46
76
|
"tape6": {
|
|
47
77
|
"tests": [
|
|
@@ -56,12 +86,13 @@
|
|
|
56
86
|
}
|
|
57
87
|
},
|
|
58
88
|
"devDependencies": {
|
|
59
|
-
"@types/node": "^25.0
|
|
60
|
-
"
|
|
61
|
-
"tape-six
|
|
89
|
+
"@types/node": "^25.3.0",
|
|
90
|
+
"prettier": "^3.8.1",
|
|
91
|
+
"tape-six": "^1.7.2",
|
|
92
|
+
"tape-six-proc": "^1.2.3",
|
|
62
93
|
"typescript": "^5.9.3"
|
|
63
94
|
},
|
|
64
95
|
"dependencies": {
|
|
65
|
-
"list-toolkit": "^2.
|
|
96
|
+
"list-toolkit": "^2.3.0"
|
|
66
97
|
}
|
|
67
98
|
}
|
package/src/CancelTaskError.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* A cancellation error that is thrown when a microtask is canceled.
|
|
3
3
|
*/
|
|
4
4
|
export declare class CancelTaskError extends Error {
|
|
5
|
-
|
|
5
|
+
name: 'CancelTaskError';
|
|
6
|
+
constructor(message?: string, options?: ErrorOptions);
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
export default CancelTaskError;
|
package/src/CancelTaskError.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
// @ts-self-types="./CancelTaskError.d.ts"
|
|
2
2
|
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
3
|
export class CancelTaskError extends Error {
|
|
6
|
-
constructor() {
|
|
7
|
-
super(
|
|
4
|
+
constructor(message = 'Task was canceled', options) {
|
|
5
|
+
super(message, options);
|
|
8
6
|
this.name = 'CancelTaskError';
|
|
9
7
|
if (Error.captureStackTrace) {
|
|
10
8
|
Error.captureStackTrace(this, CancelTaskError);
|
package/src/Counter.d.ts
CHANGED
package/src/Counter.js
CHANGED
package/src/FrameQueue.d.ts
CHANGED
|
@@ -44,7 +44,7 @@ export declare class FrameQueue extends ListQueue {
|
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Schedules a task to run in the next frame.
|
|
47
|
-
* @param fn The function to execute. If `undefined` or `null`, the task's promise will be resolved with function's arguments. Otherwise, it is resolved with the function's return value.
|
|
47
|
+
* @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.
|
|
48
48
|
* @returns The task object.
|
|
49
49
|
*/
|
|
50
50
|
schedule(
|
|
@@ -82,4 +82,4 @@ export declare class FrameQueue extends ListQueue {
|
|
|
82
82
|
*/
|
|
83
83
|
export const frameQueue: FrameQueue;
|
|
84
84
|
|
|
85
|
-
export default
|
|
85
|
+
export default frameQueue;
|
package/src/FrameQueue.js
CHANGED
package/src/IdleQueue.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export declare class IdleQueue extends ListQueue {
|
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Schedules a task to run in the next idle period.
|
|
54
|
-
* @param fn The function to execute. If `undefined` or `null`, the task's promise will be resolved with function's arguments. Otherwise, it is resolved with the function's return value.
|
|
54
|
+
* @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.
|
|
55
55
|
* @returns The task object.
|
|
56
56
|
*/
|
|
57
57
|
schedule(
|
|
@@ -96,4 +96,4 @@ export const defer: (
|
|
|
96
96
|
fn: ({deadline: IdleDeadline, task: Task, queue: IdleQueue}) => unknown
|
|
97
97
|
) => Task;
|
|
98
98
|
|
|
99
|
-
export default
|
|
99
|
+
export default idleQueue;
|
package/src/IdleQueue.js
CHANGED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {ListQueue, Task} from './ListQueue';
|
|
2
|
+
|
|
3
|
+
export {Task};
|
|
4
|
+
|
|
5
|
+
export declare class LimitedQueue extends ListQueue {
|
|
6
|
+
/**
|
|
7
|
+
* Whether the queue is paused.
|
|
8
|
+
*/
|
|
9
|
+
paused: boolean;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The function that stops the queue.
|
|
13
|
+
* It is used internally by `pause()` and `resume()`.
|
|
14
|
+
*/
|
|
15
|
+
stopQueue: (() => void) | null;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new limited queue.
|
|
19
|
+
* @param limit The maximum number of tasks that can be run in parallel.
|
|
20
|
+
* @param paused Whether the queue should start paused.
|
|
21
|
+
*/
|
|
22
|
+
constructor(limit: number, paused?: boolean);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Whether the queue is empty.
|
|
26
|
+
*/
|
|
27
|
+
get isEmpty(): boolean;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get the maximum number of tasks that can be run in parallel.
|
|
31
|
+
*/
|
|
32
|
+
get taskLimit(): number;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Set the maximum number of tasks that can be run in parallel.
|
|
36
|
+
* @param limit The new maximum number of tasks that can be run in parallel. Setting a higher limit may immediately start additional tasks.
|
|
37
|
+
*/
|
|
38
|
+
set taskLimit(limit: number);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the number of currently active tasks.
|
|
42
|
+
*/
|
|
43
|
+
get activeTasks(): number;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Whether the queue is idle.
|
|
47
|
+
*/
|
|
48
|
+
get isIdle(): boolean;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Waits for the queue to become idle.
|
|
52
|
+
* @returns A promise that resolves when the queue becomes idle. If the queue is already idle, the promise is resolved immediately.
|
|
53
|
+
*/
|
|
54
|
+
waitForIdle(): Promise<void>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Enqueues a microtask.
|
|
58
|
+
* @param fn The function to execute when the microtask is scheduled.
|
|
59
|
+
* @returns The enqueued microtask.
|
|
60
|
+
*/
|
|
61
|
+
enqueue(fn: () => unknown): Task;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Dequeues a microtask.
|
|
65
|
+
* @param task The microtask to dequeue.
|
|
66
|
+
* @returns The queue.
|
|
67
|
+
*/
|
|
68
|
+
dequeue(task: Task): this;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Schedules a microtask.
|
|
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
|
+
* @returns The task object.
|
|
74
|
+
*/
|
|
75
|
+
schedule(fn: (() => unknown) | null | undefined): Task;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Clears the queue.
|
|
79
|
+
* @returns The queue.
|
|
80
|
+
*/
|
|
81
|
+
clear(): this;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Pauses the queue.
|
|
85
|
+
* @returns The queue.
|
|
86
|
+
*/
|
|
87
|
+
pause(): this;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resumes the queue.
|
|
91
|
+
* @returns The queue.
|
|
92
|
+
*/
|
|
93
|
+
resume(): this;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Starts the queue.
|
|
97
|
+
* It is used internally by `resume()`.
|
|
98
|
+
* It is meant to be overridden in subclasses.
|
|
99
|
+
* @returns The function that stops the queue.
|
|
100
|
+
*/
|
|
101
|
+
startQueue(): (() => void) | null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default LimitedQueue;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// @ts-self-types="./LimitedQueue.d.ts"
|
|
2
|
+
|
|
3
|
+
import ListQueue from './ListQueue.js';
|
|
4
|
+
|
|
5
|
+
export class LimitedQueue extends ListQueue {
|
|
6
|
+
#taskLimit;
|
|
7
|
+
#activeTasks;
|
|
8
|
+
#idleWaiters;
|
|
9
|
+
|
|
10
|
+
constructor(limit, paused) {
|
|
11
|
+
super(paused);
|
|
12
|
+
this.#taskLimit = limit;
|
|
13
|
+
this.#activeTasks = 0;
|
|
14
|
+
this.#idleWaiters = [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get taskLimit() {
|
|
18
|
+
return this.#taskLimit;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
set taskLimit(limit) {
|
|
22
|
+
this.#taskLimit = Math.max(1, limit);
|
|
23
|
+
this.#processTasks();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get activeTasks() {
|
|
27
|
+
return this.#activeTasks;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get isIdle() {
|
|
31
|
+
return !this.#activeTasks && this.list.isEmpty;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
waitForIdle() {
|
|
35
|
+
return new Promise(resolve => {
|
|
36
|
+
if (this.isIdle) {
|
|
37
|
+
resolve();
|
|
38
|
+
} else {
|
|
39
|
+
this.#idleWaiters.push(resolve);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
startQueue() {
|
|
45
|
+
this.#processTasks();
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#processTasks() {
|
|
50
|
+
if (this.paused) return;
|
|
51
|
+
if (this.isIdle) {
|
|
52
|
+
const waiters = this.#idleWaiters;
|
|
53
|
+
this.#idleWaiters = [];
|
|
54
|
+
waiters.forEach(resolve => resolve());
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
while (this.#activeTasks < this.#taskLimit && !this.list.isEmpty) {
|
|
58
|
+
const task = this.list.popFront();
|
|
59
|
+
++this.#activeTasks;
|
|
60
|
+
LimitedQueue.wrap(() => task.fn({task, queue: this})).finally(() => {
|
|
61
|
+
--this.#activeTasks;
|
|
62
|
+
this.#processTasks();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
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
|
+
}
|
|
77
|
+
|
|
78
|
+
export default LimitedQueue;
|
package/src/ListQueue.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export declare class ListQueue extends MicroTaskQueue {
|
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Schedules a microtask.
|
|
48
|
-
* @param fn The function to execute. If `undefined` or `null`, the task's promise will be resolved with function's arguments. Otherwise, it is resolved with the function's return value.
|
|
48
|
+
* @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
49
|
* @returns The task object.
|
|
50
50
|
*/
|
|
51
51
|
schedule(fn: (() => unknown) | null | undefined): MicroTask;
|
|
@@ -82,9 +82,4 @@ export declare class ListQueue extends MicroTaskQueue {
|
|
|
82
82
|
*/
|
|
83
83
|
export declare type Task = MicroTask;
|
|
84
84
|
|
|
85
|
-
/**
|
|
86
|
-
* A task for list queues with a promise.
|
|
87
|
-
*/
|
|
88
|
-
export declare type TaskWithPromise = MicroTaskWithPromise;
|
|
89
|
-
|
|
90
85
|
export default ListQueue;
|
package/src/ListQueue.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
// @ts-self-types="./ListQueue.d.ts"
|
|
2
2
|
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
3
|
import List from 'list-toolkit/list.js';
|
|
6
4
|
import MicroTaskQueue from './MicroTaskQueue.js';
|
|
7
5
|
|
|
6
|
+
/**
|
|
7
|
+
* ListQueue extends MicroTaskQueue with linked-list task storage.
|
|
8
|
+
* AI-NOTE: This is the concrete base class most specialized queues extend.
|
|
9
|
+
* Key pattern: startQueue() returns a stop function (or null if not started).
|
|
10
|
+
* @see IdleQueue, FrameQueue, LimitedQueue, Scheduler - All extend ListQueue
|
|
11
|
+
*/
|
|
8
12
|
export class ListQueue extends MicroTaskQueue {
|
|
9
13
|
constructor(paused) {
|
|
10
14
|
super(paused);
|
|
15
|
+
// AI-NOTE: Using list-toolkit List for O(1) push/pop operations
|
|
11
16
|
this.list = new List();
|
|
17
|
+
// AI-NOTE: stopQueue holds the stop function returned by startQueue(), or null
|
|
12
18
|
this.stopQueue = null;
|
|
13
19
|
}
|
|
14
20
|
|
|
@@ -19,6 +25,7 @@ export class ListQueue extends MicroTaskQueue {
|
|
|
19
25
|
pause() {
|
|
20
26
|
if (!this.paused) {
|
|
21
27
|
super.pause();
|
|
28
|
+
// AI-NOTE: Pattern: call stop function, then null it
|
|
22
29
|
if (this.stopQueue) this.stopQueue = (this.stopQueue(), null);
|
|
23
30
|
}
|
|
24
31
|
return this;
|
|
@@ -27,6 +34,7 @@ export class ListQueue extends MicroTaskQueue {
|
|
|
27
34
|
resume() {
|
|
28
35
|
if (this.paused) {
|
|
29
36
|
super.resume();
|
|
37
|
+
// AI-NOTE: Auto-start processing if tasks exist and not already running
|
|
30
38
|
if (!this.list.isEmpty) {
|
|
31
39
|
this.stopQueue = this.startQueue();
|
|
32
40
|
}
|
|
@@ -37,6 +45,7 @@ export class ListQueue extends MicroTaskQueue {
|
|
|
37
45
|
enqueue(fn) {
|
|
38
46
|
const task = super.enqueue(fn);
|
|
39
47
|
this.list.pushBack(task);
|
|
48
|
+
// AI-NOTE: Auto-start queue on first task if not paused and not running
|
|
40
49
|
if (!this.paused && !this.stopQueue) this.stopQueue = this.startQueue();
|
|
41
50
|
return task;
|
|
42
51
|
}
|
|
@@ -44,6 +53,7 @@ export class ListQueue extends MicroTaskQueue {
|
|
|
44
53
|
dequeue(task) {
|
|
45
54
|
task.cancel();
|
|
46
55
|
this.list.removeNode(task);
|
|
56
|
+
// AI-NOTE: Auto-stop queue when empty (unless paused)
|
|
47
57
|
if (!this.paused && this.list.isEmpty && this.stopQueue)
|
|
48
58
|
this.stopQueue = (this.stopQueue(), null);
|
|
49
59
|
return this;
|
|
@@ -60,7 +70,12 @@ export class ListQueue extends MicroTaskQueue {
|
|
|
60
70
|
return this;
|
|
61
71
|
}
|
|
62
72
|
|
|
63
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Start processing the queue - MUST be overridden by subclasses.
|
|
75
|
+
* AI-NOTE: This is the abstract method pattern - base returns null.
|
|
76
|
+
* Subclasses return a function that stops the processing.
|
|
77
|
+
* @returns {Function|null} Stop function or null if not started
|
|
78
|
+
*/
|
|
64
79
|
startQueue() {
|
|
65
80
|
return null;
|
|
66
81
|
}
|
package/src/MicroTask.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ export declare class MicroTask {
|
|
|
31
31
|
*/
|
|
32
32
|
get promise(): Promise<unknown> | null;
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Whether the microtask has been settled (resolved or canceled).
|
|
36
|
+
*/
|
|
37
|
+
get settled(): boolean;
|
|
38
|
+
|
|
34
39
|
/**
|
|
35
40
|
* Resolves the microtask, if a promise is created.
|
|
36
41
|
* @param value The value to resolve the microtask with.
|
|
@@ -42,9 +47,10 @@ export declare class MicroTask {
|
|
|
42
47
|
* Cancels the microtask, if a promise is created.
|
|
43
48
|
* If the microtask is canceled, the promise will be rejected with a CancelTaskError.
|
|
44
49
|
* It can be overridden in subclasses.
|
|
50
|
+
* @param error The optional error to use as the cause of the cancellation.
|
|
45
51
|
* @returns The microtask.
|
|
46
52
|
*/
|
|
47
|
-
cancel(): this;
|
|
53
|
+
cancel(error?: Error): this;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
export default MicroTask;
|
package/src/MicroTask.js
CHANGED
|
@@ -1,43 +1,75 @@
|
|
|
1
1
|
// @ts-self-types="./MicroTask.d.ts"
|
|
2
2
|
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
3
|
import CancelTaskError from './CancelTaskError.js';
|
|
6
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Base class for deferred task execution with lazy promise creation.
|
|
7
|
+
* AI-NOTE: Promises are created lazily via makePromise() - not in constructor.
|
|
8
|
+
* This allows tasks to be created without immediate promise overhead.
|
|
9
|
+
*/
|
|
7
10
|
export class MicroTask {
|
|
8
11
|
#promise;
|
|
9
12
|
#resolve;
|
|
10
13
|
#reject;
|
|
14
|
+
#settled;
|
|
11
15
|
constructor(fn) {
|
|
12
16
|
this.fn = fn;
|
|
17
|
+
// AI-NOTE: Private fields initialized to null - lazy initialization pattern
|
|
13
18
|
this.#promise = null;
|
|
14
19
|
this.#resolve = null;
|
|
15
20
|
this.#reject = null;
|
|
21
|
+
this.#settled = false;
|
|
16
22
|
this.isCanceled = false;
|
|
17
23
|
}
|
|
24
|
+
// AI-NOTE: Returns null until makePromise() is called - this is intentional
|
|
18
25
|
get promise() {
|
|
19
26
|
return this.#promise;
|
|
20
27
|
}
|
|
28
|
+
get settled() {
|
|
29
|
+
return this.#settled;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates the promise and resolution functions.
|
|
33
|
+
* AI-NOTE: Uses Promise.withResolvers() when available (modern environments),
|
|
34
|
+
* falls back to manual Promise constructor for broader compatibility.
|
|
35
|
+
*/
|
|
21
36
|
makePromise() {
|
|
22
|
-
this.#promise
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
37
|
+
if (this.#promise) return this;
|
|
38
|
+
if (typeof Promise.withResolvers == 'function') {
|
|
39
|
+
({
|
|
40
|
+
promise: this.#promise,
|
|
41
|
+
resolve: this.#resolve,
|
|
42
|
+
reject: this.#reject
|
|
43
|
+
} = Promise.withResolvers());
|
|
44
|
+
} else {
|
|
45
|
+
this.#promise = new Promise((resolve, reject) => {
|
|
46
|
+
this.#resolve = resolve;
|
|
47
|
+
this.#reject = reject;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
26
50
|
return this;
|
|
27
51
|
}
|
|
28
52
|
resolve(value) {
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
53
|
+
if (this.#resolve) {
|
|
54
|
+
this.#resolve(value);
|
|
55
|
+
this.#resolve = null;
|
|
56
|
+
this.#reject = null;
|
|
57
|
+
this.#settled = true;
|
|
58
|
+
}
|
|
33
59
|
return this;
|
|
34
60
|
}
|
|
35
|
-
|
|
36
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Cancel the task with optional error cause.
|
|
63
|
+
* AI-NOTE: Always rejects with CancelTaskError to distinguish from other errors.
|
|
64
|
+
*/
|
|
65
|
+
cancel(error) {
|
|
37
66
|
this.isCanceled = true;
|
|
38
|
-
this.#reject
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
if (this.#reject) {
|
|
68
|
+
this.#reject(new CancelTaskError(undefined, error ? {cause: error} : undefined));
|
|
69
|
+
this.#resolve = null;
|
|
70
|
+
this.#reject = null;
|
|
71
|
+
this.#settled = true;
|
|
72
|
+
}
|
|
41
73
|
return this;
|
|
42
74
|
}
|
|
43
75
|
}
|