signalium 0.2.3 → 0.2.5
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/CHANGELOG.md +12 -0
- package/dist/config.d.ts +6 -2
- package/dist/config.js +11 -14
- package/dist/scheduling.d.ts +3 -1
- package/dist/scheduling.js +36 -26
- package/dist/signals.js +5 -4
- package/dist/weakref.d.ts +2 -0
- package/dist/weakref.js +10 -0
- package/package.json +2 -1
- package/src/__tests__/async.test.ts +5 -5
- package/src/config.ts +14 -14
- package/src/scheduling.ts +42 -34
- package/src/signals.ts +5 -4
- package/src/weakref.ts +9 -0
package/CHANGELOG.md
CHANGED
package/dist/config.d.ts
CHANGED
@@ -1,3 +1,7 @@
|
|
1
1
|
export type FlushCallback = () => Promise<void>;
|
2
|
-
export
|
3
|
-
export declare let
|
2
|
+
export type FlushFn = (fn: () => Promise<void>) => void;
|
3
|
+
export declare let scheduleFlush: FlushFn;
|
4
|
+
export declare const setScheduleFlush: (flushFn: FlushFn) => void;
|
5
|
+
export type BatchFn = (fn: () => void) => void;
|
6
|
+
export declare let runBatch: BatchFn;
|
7
|
+
export declare const setRunBatch: (batchFn: BatchFn) => void;
|
package/dist/config.js
CHANGED
@@ -1,19 +1,16 @@
|
|
1
|
-
let
|
2
|
-
let
|
3
|
-
|
4
|
-
export let scheduleWatchers = flushWatchers => {
|
5
|
-
if (currentWatcherFlush !== null)
|
1
|
+
let currentFlush = null;
|
2
|
+
export let scheduleFlush = flushWatchers => {
|
3
|
+
if (currentFlush !== null)
|
6
4
|
return;
|
7
|
-
|
8
|
-
|
5
|
+
currentFlush = setTimeout(() => {
|
6
|
+
currentFlush = null;
|
9
7
|
flushWatchers();
|
10
8
|
}, 0);
|
11
9
|
};
|
12
|
-
export
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
});
|
10
|
+
export const setScheduleFlush = (flushFn) => {
|
11
|
+
scheduleFlush = flushFn;
|
12
|
+
};
|
13
|
+
export let runBatch = fn => fn();
|
14
|
+
export const setRunBatch = (batchFn) => {
|
15
|
+
runBatch = batchFn;
|
19
16
|
};
|
package/dist/scheduling.d.ts
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
-
import
|
1
|
+
import { ComputedSignal } from './signals.js';
|
2
2
|
export declare const scheduleWatcher: (watcher: ComputedSignal<any>) => void;
|
3
|
+
export declare const scheduleDirty: (signal: ComputedSignal<any>) => void;
|
4
|
+
export declare const schedulePull: (signal: ComputedSignal<any>) => void;
|
3
5
|
export declare const scheduleDisconnect: (disconnect: ComputedSignal<any>) => void;
|
package/dist/scheduling.js
CHANGED
@@ -1,36 +1,46 @@
|
|
1
|
-
import {
|
2
|
-
let
|
3
|
-
|
4
|
-
|
1
|
+
import { scheduleFlush, runBatch } from './config.js';
|
2
|
+
let PENDING_DIRTIES = [];
|
3
|
+
let PENDING_PULLS = [];
|
4
|
+
let PENDING_WATCHERS = [];
|
5
|
+
let PENDING_DISCONNECTS = new Map();
|
6
|
+
const microtask = () => Promise.resolve();
|
5
7
|
export const scheduleWatcher = (watcher) => {
|
6
8
|
PENDING_WATCHERS.push(watcher);
|
7
|
-
|
8
|
-
let resolve;
|
9
|
-
const promise = new Promise(r => {
|
10
|
-
resolve = r;
|
11
|
-
});
|
12
|
-
PENDING_FLUSH_WATCHERS = { promise, resolve: resolve };
|
13
|
-
}
|
14
|
-
scheduleWatchers(flushWatchers);
|
9
|
+
scheduleFlush(flushWatchers);
|
15
10
|
};
|
16
|
-
const
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
PENDING_WATCHERS.length = 0;
|
11
|
+
export const scheduleDirty = (signal) => {
|
12
|
+
PENDING_DIRTIES.push(signal);
|
13
|
+
scheduleFlush(flushWatchers);
|
14
|
+
};
|
15
|
+
export const schedulePull = (signal) => {
|
16
|
+
PENDING_PULLS.push(signal);
|
17
|
+
scheduleFlush(flushWatchers);
|
24
18
|
};
|
25
19
|
export const scheduleDisconnect = (disconnect) => {
|
26
20
|
const current = PENDING_DISCONNECTS.get(disconnect) ?? 0;
|
27
21
|
PENDING_DISCONNECTS.set(disconnect, current + 1);
|
28
|
-
|
22
|
+
scheduleFlush(flushWatchers);
|
29
23
|
};
|
30
|
-
const
|
31
|
-
|
32
|
-
|
33
|
-
|
24
|
+
const flushWatchers = async () => {
|
25
|
+
while (PENDING_DIRTIES.length > 0 || PENDING_PULLS.length > 0) {
|
26
|
+
for (const dirty of PENDING_DIRTIES) {
|
27
|
+
dirty._dirtyConsumers();
|
28
|
+
}
|
29
|
+
for (const pull of PENDING_PULLS) {
|
30
|
+
pull._check();
|
31
|
+
}
|
32
|
+
PENDING_DIRTIES = [];
|
33
|
+
PENDING_PULLS = [];
|
34
|
+
await microtask();
|
34
35
|
}
|
35
|
-
|
36
|
+
runBatch(() => {
|
37
|
+
for (const watcher of PENDING_WATCHERS) {
|
38
|
+
watcher._check();
|
39
|
+
}
|
40
|
+
for (const [signal, count] of PENDING_DISCONNECTS) {
|
41
|
+
signal._disconnect(count);
|
42
|
+
}
|
43
|
+
PENDING_WATCHERS = [];
|
44
|
+
PENDING_DISCONNECTS.clear();
|
45
|
+
});
|
36
46
|
};
|
package/dist/signals.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import { scheduleDisconnect, scheduleWatcher } from './scheduling.js';
|
1
|
+
import { scheduleDirty, scheduleDisconnect, schedulePull, scheduleWatcher } from './scheduling.js';
|
2
|
+
import WeakRef from './weakref.js';
|
2
3
|
let CURRENT_ORD = 0;
|
3
4
|
let CURRENT_CONSUMER;
|
4
5
|
let CURRENT_IS_WAITING = false;
|
@@ -47,7 +48,7 @@ export class ComputedSignal {
|
|
47
48
|
const value = this._currentValue;
|
48
49
|
if (value.isPending) {
|
49
50
|
const currentConsumer = CURRENT_CONSUMER;
|
50
|
-
ACTIVE_ASYNCS.get(this)?.finally(() => currentConsumer
|
51
|
+
ACTIVE_ASYNCS.get(this)?.finally(() => schedulePull(currentConsumer));
|
51
52
|
CURRENT_IS_WAITING = true;
|
52
53
|
throw WAITING;
|
53
54
|
}
|
@@ -198,7 +199,7 @@ export class ComputedSignal {
|
|
198
199
|
value.isPending = false;
|
199
200
|
value.isSuccess = true;
|
200
201
|
this._version++;
|
201
|
-
this
|
202
|
+
scheduleDirty(this);
|
202
203
|
}, error => {
|
203
204
|
if (currentVersion !== this._version || error === WAITING) {
|
204
205
|
return;
|
@@ -207,7 +208,7 @@ export class ComputedSignal {
|
|
207
208
|
value.isPending = false;
|
208
209
|
value.isError = true;
|
209
210
|
this._version++;
|
210
|
-
this
|
211
|
+
scheduleDirty(this);
|
211
212
|
});
|
212
213
|
ACTIVE_ASYNCS.set(this, nextValue);
|
213
214
|
value.isPending = true;
|
package/dist/weakref.js
ADDED
package/package.json
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
{
|
2
2
|
"name": "signalium",
|
3
|
-
"version": "0.2.
|
3
|
+
"version": "0.2.5",
|
4
4
|
"type": "module",
|
5
5
|
"repository": "https://github.com/pzuraq/signalium",
|
6
6
|
"description": "Chain-reactivity at critical mass",
|
7
|
+
"main": "./dist/index.js",
|
7
8
|
"exports": {
|
8
9
|
".": {
|
9
10
|
"import": {
|
@@ -243,7 +243,7 @@ describe('Async Signal functionality', () => {
|
|
243
243
|
resolve: 0,
|
244
244
|
});
|
245
245
|
|
246
|
-
await sleep(
|
246
|
+
await sleep(20);
|
247
247
|
|
248
248
|
// Check to make sure we don't resolve early after the first task completes
|
249
249
|
expect(compC).toHaveValueAndCounts(result(undefined, 'pending', 'initial'), {
|
@@ -251,7 +251,7 @@ describe('Async Signal functionality', () => {
|
|
251
251
|
resolve: 0,
|
252
252
|
});
|
253
253
|
|
254
|
-
await sleep(
|
254
|
+
await sleep(20);
|
255
255
|
|
256
256
|
expect(compC).toHaveValueAndCounts(result(3, 'success', 'resolved'), {
|
257
257
|
compute: 3,
|
@@ -285,7 +285,7 @@ describe('Async Signal functionality', () => {
|
|
285
285
|
resolve: 0,
|
286
286
|
});
|
287
287
|
|
288
|
-
await sleep(
|
288
|
+
await sleep(15);
|
289
289
|
|
290
290
|
expect(compC).toHaveValueAndCounts(result(undefined, 'error', 'initial', 'error'), {
|
291
291
|
compute: 2,
|
@@ -322,7 +322,7 @@ describe('Async Signal functionality', () => {
|
|
322
322
|
resolve: 0,
|
323
323
|
});
|
324
324
|
|
325
|
-
await sleep(
|
325
|
+
await sleep(40);
|
326
326
|
|
327
327
|
expect(compC).toHaveValueAndCounts(result(undefined, 'error', 'initial', 'error'), {
|
328
328
|
compute: 2,
|
@@ -368,7 +368,7 @@ describe('Async Signal functionality', () => {
|
|
368
368
|
await sleep(30);
|
369
369
|
|
370
370
|
expect(compD).toHaveValueAndCounts(result(3, 'success', 'resolved'), {
|
371
|
-
compute:
|
371
|
+
compute: 2,
|
372
372
|
resolve: 1,
|
373
373
|
});
|
374
374
|
});
|
package/src/config.ts
CHANGED
@@ -1,27 +1,27 @@
|
|
1
|
-
let
|
2
|
-
let currentDisconnectFlush: ReturnType<typeof setTimeout> | ReturnType<typeof requestIdleCallback> | null = null;
|
1
|
+
let currentFlush: ReturnType<typeof setTimeout> | null = null;
|
3
2
|
|
4
3
|
export type FlushCallback = () => Promise<void>;
|
5
4
|
|
6
|
-
|
7
|
-
typeof requestIdleCallback === 'function' ? requestIdleCallback : (cb: () => void) => setTimeout(cb, 0);
|
5
|
+
export type FlushFn = (fn: () => Promise<void>) => void;
|
8
6
|
|
9
|
-
export let
|
10
|
-
if (
|
7
|
+
export let scheduleFlush: FlushFn = flushWatchers => {
|
8
|
+
if (currentFlush !== null) return;
|
11
9
|
|
12
|
-
|
13
|
-
|
10
|
+
currentFlush = setTimeout(() => {
|
11
|
+
currentFlush = null;
|
14
12
|
|
15
13
|
flushWatchers();
|
16
14
|
}, 0);
|
17
15
|
};
|
18
16
|
|
19
|
-
export
|
20
|
-
|
17
|
+
export const setScheduleFlush = (flushFn: FlushFn) => {
|
18
|
+
scheduleFlush = flushFn;
|
19
|
+
};
|
20
|
+
|
21
|
+
export type BatchFn = (fn: () => void) => void;
|
21
22
|
|
22
|
-
|
23
|
-
currentDisconnectFlush = null;
|
23
|
+
export let runBatch: BatchFn = fn => fn();
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
export const setRunBatch = (batchFn: BatchFn) => {
|
26
|
+
runBatch = batchFn;
|
27
27
|
};
|
package/src/scheduling.ts
CHANGED
@@ -1,39 +1,26 @@
|
|
1
|
-
import
|
2
|
-
import {
|
1
|
+
import { ComputedSignal } from './signals.js';
|
2
|
+
import { scheduleFlush, runBatch } from './config.js';
|
3
3
|
|
4
|
-
let
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
let PENDING_DIRTIES: ComputedSignal<any>[] = [];
|
5
|
+
let PENDING_PULLS: ComputedSignal<any>[] = [];
|
6
|
+
let PENDING_WATCHERS: ComputedSignal<any>[] = [];
|
7
|
+
let PENDING_DISCONNECTS = new Map<ComputedSignal<any>, number>();
|
8
8
|
|
9
|
-
const
|
10
|
-
const PENDING_DISCONNECTS: Map<ComputedSignal<any>, number> = new Map();
|
9
|
+
const microtask = () => Promise.resolve();
|
11
10
|
|
12
11
|
export const scheduleWatcher = (watcher: ComputedSignal<any>) => {
|
13
12
|
PENDING_WATCHERS.push(watcher);
|
14
|
-
|
15
|
-
if (PENDING_FLUSH_WATCHERS === null) {
|
16
|
-
let resolve: () => void;
|
17
|
-
|
18
|
-
const promise = new Promise<void>(r => {
|
19
|
-
resolve = r;
|
20
|
-
});
|
21
|
-
|
22
|
-
PENDING_FLUSH_WATCHERS = { promise, resolve: resolve! };
|
23
|
-
}
|
24
|
-
|
25
|
-
scheduleWatchers(flushWatchers);
|
13
|
+
scheduleFlush(flushWatchers);
|
26
14
|
};
|
27
15
|
|
28
|
-
const
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
while ((watcher = PENDING_WATCHERS.shift())) {
|
33
|
-
watcher._check();
|
34
|
-
}
|
16
|
+
export const scheduleDirty = (signal: ComputedSignal<any>) => {
|
17
|
+
PENDING_DIRTIES.push(signal);
|
18
|
+
scheduleFlush(flushWatchers);
|
19
|
+
};
|
35
20
|
|
36
|
-
|
21
|
+
export const schedulePull = (signal: ComputedSignal<any>) => {
|
22
|
+
PENDING_PULLS.push(signal);
|
23
|
+
scheduleFlush(flushWatchers);
|
37
24
|
};
|
38
25
|
|
39
26
|
export const scheduleDisconnect = (disconnect: ComputedSignal<any>) => {
|
@@ -41,14 +28,35 @@ export const scheduleDisconnect = (disconnect: ComputedSignal<any>) => {
|
|
41
28
|
|
42
29
|
PENDING_DISCONNECTS.set(disconnect, current + 1);
|
43
30
|
|
44
|
-
|
31
|
+
scheduleFlush(flushWatchers);
|
45
32
|
};
|
46
33
|
|
47
|
-
const
|
48
|
-
|
49
|
-
|
50
|
-
|
34
|
+
const flushWatchers = async () => {
|
35
|
+
while (PENDING_DIRTIES.length > 0 || PENDING_PULLS.length > 0) {
|
36
|
+
for (const dirty of PENDING_DIRTIES) {
|
37
|
+
dirty._dirtyConsumers();
|
38
|
+
}
|
39
|
+
|
40
|
+
for (const pull of PENDING_PULLS) {
|
41
|
+
pull._check();
|
42
|
+
}
|
43
|
+
|
44
|
+
PENDING_DIRTIES = [];
|
45
|
+
PENDING_PULLS = [];
|
46
|
+
|
47
|
+
await microtask();
|
51
48
|
}
|
52
49
|
|
53
|
-
|
50
|
+
runBatch(() => {
|
51
|
+
for (const watcher of PENDING_WATCHERS) {
|
52
|
+
watcher._check();
|
53
|
+
}
|
54
|
+
|
55
|
+
for (const [signal, count] of PENDING_DISCONNECTS) {
|
56
|
+
signal._disconnect(count);
|
57
|
+
}
|
58
|
+
|
59
|
+
PENDING_WATCHERS = [];
|
60
|
+
PENDING_DISCONNECTS.clear();
|
61
|
+
});
|
54
62
|
};
|
package/src/signals.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import { scheduleDisconnect, scheduleWatcher } from './scheduling.js';
|
1
|
+
import { scheduleDirty, scheduleDisconnect, schedulePull, scheduleWatcher } from './scheduling.js';
|
2
|
+
import WeakRef from './weakref.js';
|
2
3
|
|
3
4
|
let CURRENT_ORD = 0;
|
4
5
|
let CURRENT_CONSUMER: ComputedSignal<any> | undefined;
|
@@ -125,7 +126,7 @@ export class ComputedSignal<T> {
|
|
125
126
|
|
126
127
|
if (value.isPending) {
|
127
128
|
const currentConsumer = CURRENT_CONSUMER;
|
128
|
-
ACTIVE_ASYNCS.get(this)?.finally(() => currentConsumer
|
129
|
+
ACTIVE_ASYNCS.get(this)?.finally(() => schedulePull(currentConsumer));
|
129
130
|
|
130
131
|
CURRENT_IS_WAITING = true;
|
131
132
|
throw WAITING;
|
@@ -305,7 +306,7 @@ export class ComputedSignal<T> {
|
|
305
306
|
value.isSuccess = true;
|
306
307
|
|
307
308
|
this._version++;
|
308
|
-
this
|
309
|
+
scheduleDirty(this);
|
309
310
|
},
|
310
311
|
error => {
|
311
312
|
if (currentVersion !== this._version || error === WAITING) {
|
@@ -316,7 +317,7 @@ export class ComputedSignal<T> {
|
|
316
317
|
value.isPending = false;
|
317
318
|
value.isError = true;
|
318
319
|
this._version++;
|
319
|
-
this
|
320
|
+
scheduleDirty(this);
|
320
321
|
},
|
321
322
|
);
|
322
323
|
|
package/src/weakref.ts
ADDED