@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 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
- getStream() {
36
- return new DependencyStream(this);
36
+ get done() {
37
+ return this.abortPromise.isFulfilled;
37
38
  }
38
39
  [Symbol.asyncIterator](thisStreamConfig = {}) {
39
- const totalDispose = this.abortPromise;
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(this.value);
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 isDisposed;
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
- this.reactionPromise.promise,
62
+ owner.next(),
64
63
  ]);
65
- if (totalDispose.isFulfilled || thisStreamConfig.externalDispose?.isFulfilled) {
66
- isDisposed = true;
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
- async next(ref = {}) {
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
- let race = this._race;
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 (abortPromise.isFulfilled) {
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
  }
@@ -20,5 +20,7 @@ export function runFnWithDepCollection(fn) {
20
20
  export function collectDep(dep) {
21
21
  if (!global.watchFlag)
22
22
  return;
23
+ if (dep.done)
24
+ return;
23
25
  global.dependencies.add(dep);
24
26
  }
@@ -0,0 +1,4 @@
1
+ import { DependencyStream } from "../dependency.stream.js";
2
+ export function getStream(dep) {
3
+ return new DependencyStream(dep);
4
+ }
@@ -1,6 +1,7 @@
1
1
  import { symAI } from "../../constants.js";
2
+ import { getStream } from "./get.stream.js";
2
3
  export function onceStream(dep) {
3
- const stream = dep.getStream();
4
+ const stream = getStream(dep);
4
5
  const iterator = stream[symAI]();
5
6
  return {
6
7
  get isDisposed() {
@@ -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
- return {
9
+ let depsArray;
10
+ let beforeValues;
11
+ let obj = {
11
12
  next: async () => {
12
- await Promise
13
- .race(Array
14
- .from(deps)
15
- .map(dep => dep.next(ref)));
16
- if (ref.done)
17
- return ref;
18
- ({ result, deps } = runFnWithDepCollection(fn));
19
- return { done: false, value: result };
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
- getStream(this: Dependency<T>): DependencyStream<T>;
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
- next(ref?: {
16
- done?: boolean;
17
- }): Promise<{
18
+ private getOrCreateRace;
19
+ /**
20
+ * Another subscribe for current race
21
+ */
22
+ next(): Promise<{
18
23
  done: true;
19
24
  value?: never;
20
25
  } | {
@@ -0,0 +1,3 @@
1
+ import { DependencyStream } from "../dependency.stream.ts";
2
+ import { Dependency } from "../dependency.ts";
3
+ export declare function getStream<T>(dep: Dependency<T>): DependencyStream<T>;
@@ -7,7 +7,7 @@ export declare function reaction<T>(fn: () => T): {
7
7
  done: true;
8
8
  value?: never;
9
9
  } | {
10
- done: boolean;
10
+ done: false;
11
11
  value: T;
12
12
  }>;
13
13
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fbltd/async",
3
- "version": "1.0.24",
3
+ "version": "1.0.25",
4
4
  "description": "Miscellaneous async utils",
5
5
  "homepage": "https://github.com/GlennMiller1991/async",
6
6
  "type": "module",