@fbltd/async 1.0.24 → 1.0.26
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 +27 -1
- package/dist/bin/dependency/dep.factory.js +10 -0
- package/dist/bin/dependency/dependency.js +30 -20
- package/dist/bin/dependency/global.js +2 -0
- package/dist/bin/dependency/index.js +1 -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 +41 -21
- package/dist/types/dependency/contracts.d.ts +13 -0
- package/dist/types/dependency/dep.factory.d.ts +6 -0
- package/dist/types/dependency/dependency.d.ts +13 -6
- package/dist/types/dependency/index.d.ts +1 -0
- package/dist/types/dependency/stream-utils/get.stream.d.ts +3 -0
- package/dist/types/dependency/stream-utils/reaction.d.ts +3 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,6 @@ There is no any management of passed data for resolving.
|
|
|
36
36
|
Returned promise is usual ES promise so it is impossible to fulfill promise twice.
|
|
37
37
|
|
|
38
38
|
### DependencyStream
|
|
39
|
-
#### core
|
|
40
39
|
Implementation of reactive model leveraging native JavaScript async features like
|
|
41
40
|
Promises, (async) iterators and generators.
|
|
42
41
|
The version is 0.0.x so keep it in mind
|
|
@@ -71,7 +70,34 @@ setInterval(() => {
|
|
|
71
70
|
##### onceStream
|
|
72
71
|
##### raceStream
|
|
73
72
|
##### next
|
|
73
|
+
|
|
74
74
|
##### reaction
|
|
75
|
+
The most powerful util among all stream utils.
|
|
76
|
+
Function is watching only for actual dependencies and does not react
|
|
77
|
+
for dependencies that do not affect result value. Since the reaction
|
|
78
|
+
provides dependency instance, of course, there is a function result caching:
|
|
79
|
+
```typescript
|
|
80
|
+
let isDep1Ready = new Dependency(false);
|
|
81
|
+
let isDep2Ready = new Dependency(false);
|
|
82
|
+
let counter = new Dependency(0);
|
|
83
|
+
|
|
84
|
+
// function that uses dependency instances
|
|
85
|
+
let watchFn = () => {
|
|
86
|
+
// Take a look at IF condition -
|
|
87
|
+
// value can be changed only if dep1 and dep2 is ready
|
|
88
|
+
if (isDep1Ready.value && isDep2Ready.value) {
|
|
89
|
+
return counter.value;
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function subscribe() {
|
|
95
|
+
for await (const value of reaction(watchFn)) {
|
|
96
|
+
reactionFn();
|
|
97
|
+
}
|
|
98
|
+
exitFn();
|
|
99
|
+
}
|
|
100
|
+
```
|
|
75
101
|
|
|
76
102
|
#### Framework integrations
|
|
77
103
|
##### React
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Dependency } from "./dependency.js";
|
|
2
|
+
import { reaction } from "./stream-utils/index.js";
|
|
3
|
+
export class DepFactory {
|
|
4
|
+
static ofValue(value, config) {
|
|
5
|
+
return new Dependency(value, config);
|
|
6
|
+
}
|
|
7
|
+
static ofReaction(fn, config) {
|
|
8
|
+
return reaction(fn, config);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
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";
|
|
4
|
+
import { symAI } from "../constants.js";
|
|
5
5
|
export class Dependency {
|
|
6
6
|
_value;
|
|
7
7
|
reactionPromise;
|
|
@@ -16,6 +16,8 @@ export class Dependency {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
_set(v) {
|
|
19
|
+
if (this.done)
|
|
20
|
+
return;
|
|
19
21
|
if (this.config.withCustomEquality(this._value, v)) {
|
|
20
22
|
return;
|
|
21
23
|
}
|
|
@@ -32,38 +34,36 @@ export class Dependency {
|
|
|
32
34
|
collectDep(this);
|
|
33
35
|
return this._value;
|
|
34
36
|
}
|
|
35
|
-
|
|
36
|
-
return
|
|
37
|
+
get done() {
|
|
38
|
+
return this.abortPromise.isFulfilled;
|
|
37
39
|
}
|
|
38
|
-
[
|
|
39
|
-
const
|
|
40
|
-
const externalPromises = [totalDispose.promise];
|
|
40
|
+
[symAI](thisStreamConfig = {}) {
|
|
41
|
+
const externalPromises = [];
|
|
41
42
|
let firstPromise;
|
|
42
43
|
const withReactionOnSubscribe = this.config.withReactionOnSubscribe || thisStreamConfig.withReactionOnSubscribe;
|
|
43
44
|
if (withReactionOnSubscribe) {
|
|
44
45
|
firstPromise = new PromiseConfiguration();
|
|
45
|
-
firstPromise.resolve(
|
|
46
|
+
firstPromise.resolve();
|
|
46
47
|
externalPromises.push(firstPromise.promise);
|
|
47
48
|
}
|
|
48
49
|
if (thisStreamConfig.externalDispose) {
|
|
49
50
|
externalPromises.push(thisStreamConfig.externalDispose.promise);
|
|
50
51
|
}
|
|
51
|
-
let isDisposed = false;
|
|
52
52
|
const owner = this;
|
|
53
|
+
let done = false;
|
|
53
54
|
return {
|
|
54
55
|
owner,
|
|
55
56
|
dispose: owner.dispose.bind(owner),
|
|
56
57
|
get isDisposed() {
|
|
57
|
-
return
|
|
58
|
+
return done || owner.done;
|
|
58
59
|
},
|
|
59
60
|
next: async () => {
|
|
60
|
-
this.reactionPromise = this.reactionPromise ?? new PromiseConfiguration();
|
|
61
61
|
await Promise.race([
|
|
62
62
|
...externalPromises,
|
|
63
|
-
|
|
63
|
+
owner.next(),
|
|
64
64
|
]);
|
|
65
|
-
if (
|
|
66
|
-
|
|
65
|
+
if (this.done || thisStreamConfig.externalDispose?.isFulfilled) {
|
|
66
|
+
done = true;
|
|
67
67
|
return { done: true };
|
|
68
68
|
}
|
|
69
69
|
if (firstPromise) {
|
|
@@ -79,21 +79,29 @@ export class Dependency {
|
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* One race of value change and dependency dispose
|
|
84
|
+
* for all subscribers
|
|
85
|
+
*/
|
|
82
86
|
_race;
|
|
83
|
-
|
|
84
|
-
const abortPromise = this.abortPromise;
|
|
87
|
+
getOrCreateRace() {
|
|
85
88
|
if (!this._race) {
|
|
86
89
|
this.reactionPromise = this.reactionPromise ?? new PromiseConfiguration();
|
|
87
90
|
this._race = Promise.race([
|
|
88
|
-
abortPromise.promise,
|
|
91
|
+
this.abortPromise.promise,
|
|
89
92
|
this.reactionPromise.promise,
|
|
90
93
|
]);
|
|
91
94
|
}
|
|
92
|
-
|
|
95
|
+
return this._race;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Another subscribe for current race
|
|
99
|
+
*/
|
|
100
|
+
async next() {
|
|
101
|
+
let race = this.getOrCreateRace();
|
|
93
102
|
await race;
|
|
94
103
|
this._race = undefined;
|
|
95
|
-
if (
|
|
96
|
-
ref.done = true;
|
|
104
|
+
if (this.done) {
|
|
97
105
|
return { done: true };
|
|
98
106
|
}
|
|
99
107
|
return {
|
|
@@ -103,9 +111,11 @@ export class Dependency {
|
|
|
103
111
|
}
|
|
104
112
|
};
|
|
105
113
|
}
|
|
114
|
+
get disposePromise() {
|
|
115
|
+
return this.abortPromise.promise;
|
|
116
|
+
}
|
|
106
117
|
dispose() {
|
|
107
118
|
this.abortPromise.resolve();
|
|
108
|
-
this.abortPromise = new PromiseConfiguration();
|
|
109
119
|
this.reactionPromise = undefined;
|
|
110
120
|
}
|
|
111
121
|
}
|
|
@@ -1,24 +1,44 @@
|
|
|
1
1
|
import { runFnWithDepCollection } from "../global.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
[
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.
|
|
14
|
-
.
|
|
15
|
-
.map(dep => dep.next(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
import { Dependency } from "../dependency.js";
|
|
3
|
+
import { symAI } from "../../constants.js";
|
|
4
|
+
export function reaction(fn, config) {
|
|
5
|
+
let { result, deps } = runFnWithDepCollection(fn);
|
|
6
|
+
const dep = new Dependency(result, config);
|
|
7
|
+
async function subscribe() {
|
|
8
|
+
const stream = { [symAI]: () => {
|
|
9
|
+
let depsArray;
|
|
10
|
+
let beforeValues;
|
|
11
|
+
let obj = {
|
|
12
|
+
next: async () => {
|
|
13
|
+
depsArray = Array.from(deps);
|
|
14
|
+
beforeValues = depsArray.map(dep => dep.value);
|
|
15
|
+
const promises = depsArray.map(dep => dep.next());
|
|
16
|
+
promises.push(dep.disposePromise);
|
|
17
|
+
await Promise.race(depsArray.map(dep => dep.next()));
|
|
18
|
+
if (dep.done)
|
|
19
|
+
return { done: true };
|
|
20
|
+
let shouldRun = depsArray.some((dep, i) => dep.value !== beforeValues[i]);
|
|
21
|
+
if (shouldRun) {
|
|
22
|
+
({ result, deps } = runFnWithDepCollection(fn));
|
|
23
|
+
return { done: false, value: result };
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
for (let dep of deps) {
|
|
27
|
+
if (dep.done)
|
|
28
|
+
deps.delete(dep);
|
|
29
|
+
}
|
|
30
|
+
if (!deps.size)
|
|
31
|
+
return { done: true };
|
|
32
|
+
return obj.next();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return obj;
|
|
37
|
+
} };
|
|
38
|
+
for await (let value of stream) {
|
|
39
|
+
dep.value = value;
|
|
22
40
|
}
|
|
23
|
-
}
|
|
41
|
+
}
|
|
42
|
+
subscribe().then(() => dep.dispose());
|
|
43
|
+
return dep;
|
|
24
44
|
}
|
|
@@ -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
|
}>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Dependency } from "./dependency.ts";
|
|
2
|
+
import { IAllStreamConfig } from "./contracts.ts";
|
|
3
|
+
export declare abstract class DepFactory {
|
|
4
|
+
static ofValue<T>(value: T, config?: Partial<IAllStreamConfig<T>>): Dependency<T>;
|
|
5
|
+
static ofReaction<T>(fn: () => T, config?: Partial<IAllStreamConfig<T>>): Dependency<T>;
|
|
6
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IAllStreamConfig, IStreamIterator, IThisStreamConfig } from "./contracts.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { symAI } from "../constants.ts";
|
|
3
3
|
export declare class Dependency<T = any> {
|
|
4
4
|
private _value;
|
|
5
5
|
private reactionPromise;
|
|
@@ -9,17 +9,24 @@ export declare class Dependency<T = any> {
|
|
|
9
9
|
private _set;
|
|
10
10
|
set value(v: T);
|
|
11
11
|
get value(): T;
|
|
12
|
-
|
|
13
|
-
[
|
|
12
|
+
get done(): boolean;
|
|
13
|
+
[symAI](this: Dependency<T>, thisStreamConfig?: IThisStreamConfig): IStreamIterator<T>;
|
|
14
|
+
/**
|
|
15
|
+
* One race of value change and dependency dispose
|
|
16
|
+
* for all subscribers
|
|
17
|
+
*/
|
|
14
18
|
private _race;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
private getOrCreateRace;
|
|
20
|
+
/**
|
|
21
|
+
* Another subscribe for current race
|
|
22
|
+
*/
|
|
23
|
+
next(): Promise<{
|
|
18
24
|
done: true;
|
|
19
25
|
value?: never;
|
|
20
26
|
} | {
|
|
21
27
|
done: boolean;
|
|
22
28
|
readonly value: T;
|
|
23
29
|
}>;
|
|
30
|
+
get disposePromise(): Promise<void>;
|
|
24
31
|
dispose(this: Dependency<T>): void;
|
|
25
32
|
}
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export declare function reaction<T>(fn: () => T): {
|
|
5
|
-
[Symbol.asyncIterator]: () => {
|
|
6
|
-
next: () => Promise<{
|
|
7
|
-
done: true;
|
|
8
|
-
value?: never;
|
|
9
|
-
} | {
|
|
10
|
-
done: boolean;
|
|
11
|
-
value: T;
|
|
12
|
-
}>;
|
|
13
|
-
};
|
|
14
|
-
};
|
|
1
|
+
import { Dependency } from "../dependency.ts";
|
|
2
|
+
import { IAllStreamConfig } from "../contracts.js";
|
|
3
|
+
export declare function reaction<T>(fn: () => T, config?: Partial<IAllStreamConfig<T>>): Dependency<T>;
|