@fbltd/async 1.0.24 → 1.0.25
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 +28 -0
- package/dist/bin/dependency/dependency.js +25 -19
- package/dist/bin/dependency/global.js +2 -0
- package/dist/bin/dependency/stream-utils/get.stream.js +4 -0
- package/dist/bin/dependency/stream-utils/once.stream.js +2 -1
- package/dist/bin/dependency/stream-utils/reaction.js +21 -10
- package/dist/types/dependency/contracts.d.ts +13 -0
- package/dist/types/dependency/dependency.d.ts +10 -5
- package/dist/types/dependency/stream-utils/get.stream.d.ts +3 -0
- package/dist/types/dependency/stream-utils/reaction.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,7 +71,35 @@ setInterval(() => {
|
|
|
71
71
|
##### onceStream
|
|
72
72
|
##### raceStream
|
|
73
73
|
##### next
|
|
74
|
+
|
|
74
75
|
##### reaction
|
|
76
|
+
The most powerful util among all stream utils.
|
|
77
|
+
Function is watching only for actual dependencies and does not react
|
|
78
|
+
for dependencies that do not affect result value:
|
|
79
|
+
```typescript
|
|
80
|
+
let isDep1Ready = new Dependency(false);
|
|
81
|
+
let isDep2Ready = new Dependency(false);
|
|
82
|
+
let counter = new Dependency(0);
|
|
83
|
+
|
|
84
|
+
// function that implicityly uses dependency instances
|
|
85
|
+
let cachedFunction = () => {
|
|
86
|
+
if (isDep1Ready.value && isDep2Ready.value) {
|
|
87
|
+
return counter.value;
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function subscribe() {
|
|
93
|
+
for await (const value of reaction(cachedFunction)) {
|
|
94
|
+
reactionFn();
|
|
95
|
+
}
|
|
96
|
+
exitFn();
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
The reaction is declared deprecated because a return value caching does not
|
|
100
|
+
use optimal strategy. Moreover, ideally, reaction should give back a
|
|
101
|
+
dependency instance or something like it, that can provide an ability
|
|
102
|
+
to be an observable for another reaction.
|
|
75
103
|
|
|
76
104
|
#### Framework integrations
|
|
77
105
|
##### React
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { PromiseConfiguration } from "../promise-configuration.js";
|
|
2
|
-
import { DependencyStream } from "./dependency.stream.js";
|
|
3
2
|
import { baseComparer } from "./utils.js";
|
|
4
3
|
import { collectDep } from "./global.js";
|
|
5
4
|
export class Dependency {
|
|
@@ -16,6 +15,8 @@ export class Dependency {
|
|
|
16
15
|
};
|
|
17
16
|
}
|
|
18
17
|
_set(v) {
|
|
18
|
+
if (this.done)
|
|
19
|
+
return;
|
|
19
20
|
if (this.config.withCustomEquality(this._value, v)) {
|
|
20
21
|
return;
|
|
21
22
|
}
|
|
@@ -32,38 +33,36 @@ export class Dependency {
|
|
|
32
33
|
collectDep(this);
|
|
33
34
|
return this._value;
|
|
34
35
|
}
|
|
35
|
-
|
|
36
|
-
return
|
|
36
|
+
get done() {
|
|
37
|
+
return this.abortPromise.isFulfilled;
|
|
37
38
|
}
|
|
38
39
|
[Symbol.asyncIterator](thisStreamConfig = {}) {
|
|
39
|
-
const
|
|
40
|
-
const externalPromises = [totalDispose.promise];
|
|
40
|
+
const externalPromises = [];
|
|
41
41
|
let firstPromise;
|
|
42
42
|
const withReactionOnSubscribe = this.config.withReactionOnSubscribe || thisStreamConfig.withReactionOnSubscribe;
|
|
43
43
|
if (withReactionOnSubscribe) {
|
|
44
44
|
firstPromise = new PromiseConfiguration();
|
|
45
|
-
firstPromise.resolve(
|
|
45
|
+
firstPromise.resolve();
|
|
46
46
|
externalPromises.push(firstPromise.promise);
|
|
47
47
|
}
|
|
48
48
|
if (thisStreamConfig.externalDispose) {
|
|
49
49
|
externalPromises.push(thisStreamConfig.externalDispose.promise);
|
|
50
50
|
}
|
|
51
|
-
let isDisposed = false;
|
|
52
51
|
const owner = this;
|
|
52
|
+
let done = false;
|
|
53
53
|
return {
|
|
54
54
|
owner,
|
|
55
55
|
dispose: owner.dispose.bind(owner),
|
|
56
56
|
get isDisposed() {
|
|
57
|
-
return
|
|
57
|
+
return done || owner.done;
|
|
58
58
|
},
|
|
59
59
|
next: async () => {
|
|
60
|
-
this.reactionPromise = this.reactionPromise ?? new PromiseConfiguration();
|
|
61
60
|
await Promise.race([
|
|
62
61
|
...externalPromises,
|
|
63
|
-
|
|
62
|
+
owner.next(),
|
|
64
63
|
]);
|
|
65
|
-
if (
|
|
66
|
-
|
|
64
|
+
if (this.done || thisStreamConfig.externalDispose?.isFulfilled) {
|
|
65
|
+
done = true;
|
|
67
66
|
return { done: true };
|
|
68
67
|
}
|
|
69
68
|
if (firstPromise) {
|
|
@@ -79,21 +78,29 @@ export class Dependency {
|
|
|
79
78
|
}
|
|
80
79
|
};
|
|
81
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* One race of value change and dependency dispose
|
|
83
|
+
* for all subscribers
|
|
84
|
+
*/
|
|
82
85
|
_race;
|
|
83
|
-
|
|
84
|
-
const abortPromise = this.abortPromise;
|
|
86
|
+
getOrCreateRace() {
|
|
85
87
|
if (!this._race) {
|
|
86
88
|
this.reactionPromise = this.reactionPromise ?? new PromiseConfiguration();
|
|
87
89
|
this._race = Promise.race([
|
|
88
|
-
abortPromise.promise,
|
|
90
|
+
this.abortPromise.promise,
|
|
89
91
|
this.reactionPromise.promise,
|
|
90
92
|
]);
|
|
91
93
|
}
|
|
92
|
-
|
|
94
|
+
return this._race;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Another subscribe for current race
|
|
98
|
+
*/
|
|
99
|
+
async next() {
|
|
100
|
+
let race = this.getOrCreateRace();
|
|
93
101
|
await race;
|
|
94
102
|
this._race = undefined;
|
|
95
|
-
if (
|
|
96
|
-
ref.done = true;
|
|
103
|
+
if (this.done) {
|
|
97
104
|
return { done: true };
|
|
98
105
|
}
|
|
99
106
|
return {
|
|
@@ -105,7 +112,6 @@ export class Dependency {
|
|
|
105
112
|
}
|
|
106
113
|
dispose() {
|
|
107
114
|
this.abortPromise.resolve();
|
|
108
|
-
this.abortPromise = new PromiseConfiguration();
|
|
109
115
|
this.reactionPromise = undefined;
|
|
110
116
|
}
|
|
111
117
|
}
|
|
@@ -3,22 +3,33 @@ import { runFnWithDepCollection } from "../global.js";
|
|
|
3
3
|
* @deprecated
|
|
4
4
|
*/
|
|
5
5
|
export function reaction(fn) {
|
|
6
|
-
const ref = { done: false };
|
|
7
6
|
return {
|
|
8
7
|
[Symbol.asyncIterator]: () => {
|
|
9
8
|
let { result, deps } = runFnWithDepCollection(fn);
|
|
10
|
-
|
|
9
|
+
let depsArray;
|
|
10
|
+
let beforeValues;
|
|
11
|
+
let obj = {
|
|
11
12
|
next: async () => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
depsArray = Array.from(deps);
|
|
14
|
+
beforeValues = depsArray.map(dep => dep.value);
|
|
15
|
+
await Promise.race(depsArray.map(dep => dep.next()));
|
|
16
|
+
let shouldRun = depsArray.some((dep, i) => dep.value !== beforeValues[i]);
|
|
17
|
+
if (shouldRun) {
|
|
18
|
+
({ result, deps } = runFnWithDepCollection(fn));
|
|
19
|
+
return { done: false, value: result };
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
for (let dep of deps) {
|
|
23
|
+
if (dep.done)
|
|
24
|
+
deps.delete(dep);
|
|
25
|
+
}
|
|
26
|
+
if (!deps.size)
|
|
27
|
+
return { done: true };
|
|
28
|
+
return obj.next();
|
|
29
|
+
}
|
|
20
30
|
}
|
|
21
31
|
};
|
|
32
|
+
return obj;
|
|
22
33
|
}
|
|
23
34
|
};
|
|
24
35
|
}
|
|
@@ -16,9 +16,22 @@ export interface IIsEquals<T> {
|
|
|
16
16
|
}
|
|
17
17
|
export type IAllStreamConfig<T> = {
|
|
18
18
|
withCustomEquality: IIsEquals<T>;
|
|
19
|
+
/**
|
|
20
|
+
* Reaction happens anyway right after current task
|
|
21
|
+
* wherever dependency was disposed
|
|
22
|
+
*/
|
|
19
23
|
withReactionOnSubscribe: boolean;
|
|
20
24
|
};
|
|
21
25
|
export type IThisStreamConfig = Partial<{
|
|
26
|
+
/**
|
|
27
|
+
* Reaction happens right after current task
|
|
28
|
+
* wherever dependency itself was disposed, but
|
|
29
|
+
* stream dispose has priority over first reaction
|
|
30
|
+
*/
|
|
22
31
|
withReactionOnSubscribe: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Dispose happens anyway right after current task,
|
|
34
|
+
* wherever value of dependency was changed
|
|
35
|
+
*/
|
|
23
36
|
externalDispose: PromiseConfiguration<any>;
|
|
24
37
|
}>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { IAllStreamConfig, IStreamIterator, IThisStreamConfig } from "./contracts.ts";
|
|
2
|
-
import { DependencyStream } from "./dependency.stream.ts";
|
|
3
2
|
export declare class Dependency<T = any> {
|
|
4
3
|
private _value;
|
|
5
4
|
private reactionPromise;
|
|
@@ -9,12 +8,18 @@ export declare class Dependency<T = any> {
|
|
|
9
8
|
private _set;
|
|
10
9
|
set value(v: T);
|
|
11
10
|
get value(): T;
|
|
12
|
-
|
|
11
|
+
get done(): boolean;
|
|
13
12
|
[Symbol.asyncIterator](this: Dependency<T>, thisStreamConfig?: IThisStreamConfig): IStreamIterator<T>;
|
|
13
|
+
/**
|
|
14
|
+
* One race of value change and dependency dispose
|
|
15
|
+
* for all subscribers
|
|
16
|
+
*/
|
|
14
17
|
private _race;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
private getOrCreateRace;
|
|
19
|
+
/**
|
|
20
|
+
* Another subscribe for current race
|
|
21
|
+
*/
|
|
22
|
+
next(): Promise<{
|
|
18
23
|
done: true;
|
|
19
24
|
value?: never;
|
|
20
25
|
} | {
|