5
5
State ,
6
6
StateConfigInterface ,
7
7
StateIngestConfigInterface ,
8
+ StateObserver ,
8
9
} from '../state' ;
9
10
import { Observer } from '../runtime' ;
10
11
import { ComputedTracker } from './computed.tracker' ;
@@ -15,8 +16,19 @@ export class Computed<
15
16
> extends State < ComputedValueType > {
16
17
public config : ComputedConfigInterface ;
17
18
19
+ // Caches if the compute function is async
20
+ private computeFunctionIsAsync ! : boolean ;
21
+
18
22
// Function to compute the Computed Class value
19
- public computeFunction : ComputeFunctionType < ComputedValueType > ;
23
+ private _computeFunction ! : ComputeFunctionType < ComputedValueType > ;
24
+ public get computeFunction ( ) : ComputeFunctionType < ComputedValueType > {
25
+ return this . _computeFunction ;
26
+ }
27
+ public set computeFunction ( v : ComputeFunctionType < ComputedValueType > ) {
28
+ this . _computeFunction = v ;
29
+ this . computeFunctionIsAsync = isAsyncFunction ( v ) ;
30
+ }
31
+
20
32
// All dependencies the Computed Class depends on (including hardCoded and automatically detected dependencies)
21
33
public deps : Set < Observer > = new Set ( ) ;
22
34
// Only hardCoded dependencies the Computed Class depends on
@@ -60,12 +72,13 @@ export class Computed<
60
72
dependents : config . dependents ,
61
73
}
62
74
) ;
75
+ this . computeFunction = computeFunction ;
76
+
63
77
config = defineConfig ( config , {
64
78
computedDeps : [ ] ,
65
- autodetect : ! isAsyncFunction ( computeFunction ) ,
79
+ autodetect : ! this . computeFunctionIsAsync ,
66
80
} ) ;
67
81
this . agileInstance = ( ) => agileInstance ;
68
- this . computeFunction = computeFunction ;
69
82
this . config = {
70
83
autodetect : config . autodetect as any ,
71
84
} ;
@@ -86,6 +99,64 @@ export class Computed<
86
99
this . recompute ( { autodetect : config . autodetect , overwrite : true } ) ;
87
100
}
88
101
102
+ /**
103
+ * synchronously computes the value
104
+ *
105
+ * @param config ComputeConfigInterface
106
+ * @returns
107
+ */
108
+ private computeSync ( config : ComputeConfigInterface = { } ) : ComputedValueType {
109
+ config = defineConfig ( config , {
110
+ autodetect : this . config . autodetect ,
111
+ } ) ;
112
+
113
+ // Start auto tracking of Observers on which the computeFunction might depend
114
+ if ( config . autodetect ) ComputedTracker . track ( ) ;
115
+
116
+ const computeFunction = this . computeFunction as SyncComputeFunctionType < ComputedValueType > ;
117
+ const computedValue = computeFunction ( ) ;
118
+
119
+ // Handle auto tracked Observers
120
+ if ( config . autodetect ) {
121
+ const foundDeps = ComputedTracker . getTrackedObservers ( ) ;
122
+
123
+ // Clean up old dependencies
124
+ this . deps . forEach ( ( observer ) => {
125
+ if (
126
+ ! foundDeps . includes ( observer ) &&
127
+ ! this . hardCodedDeps . includes ( observer )
128
+ ) {
129
+ this . deps . delete ( observer ) ;
130
+ observer . removeDependent ( this . observers [ 'value' ] ) ;
131
+ }
132
+ } ) ;
133
+
134
+ // Make this Observer depend on the newly found dep Observers
135
+ foundDeps . forEach ( ( observer ) => {
136
+ if ( ! this . deps . has ( observer ) ) {
137
+ this . deps . add ( observer ) ;
138
+ observer . addDependent ( this . observers [ 'value' ] ) ;
139
+ }
140
+ } ) ;
141
+ }
142
+
143
+ return computedValue ;
144
+ }
145
+
146
+ /**
147
+ * asynchronously computes the value
148
+ *
149
+ * @param config ComputeConfigInterface
150
+ * @returns
151
+ */
152
+ private async computeAsync ( config : ComputeConfigInterface = { } ) : Promise < ComputedValueType > {
153
+ config = defineConfig ( config , {
154
+ autodetect : this . config . autodetect ,
155
+ } ) ;
156
+
157
+ return this . computeFunction ( ) ;
158
+ }
159
+
89
160
/**
90
161
* Forces a recomputation of the cached value with the compute function.
91
162
*
@@ -98,12 +169,31 @@ export class Computed<
98
169
config = defineConfig ( config , {
99
170
autodetect : false ,
100
171
} ) ;
101
- this . compute ( { autodetect : config . autodetect } ) . then ( ( result ) => {
102
- this . observers [ 'value' ] . ingestValue ( result , config ) ;
103
- } ) ;
172
+
173
+ this . computeAndIngest ( this . observers [ 'value' ] , config , { autodetect : config . autodetect } ) ;
174
+
104
175
return this ;
105
176
}
106
177
178
+ /**
179
+ * Recomputes value and ingests it into the observer
180
+ *
181
+ * @public
182
+ * @param observer - StateObserver<ComputedValueType> to ingest value into
183
+ * @param ingestConfig - Configuration object
184
+ */
185
+ public computeAndIngest ( observer : StateObserver < ComputedValueType > , ingestConfig : StateIngestConfigInterface , computeConfig : ComputeConfigInterface = { } ) {
186
+ if ( this . computeFunctionIsAsync ) {
187
+ this . computeAsync ( computeConfig ) . then ( ( result ) => {
188
+ observer . ingestValue ( result , ingestConfig ) ;
189
+ } ) ;
190
+ }
191
+ else {
192
+ const result = this . computeSync ( computeConfig ) ;
193
+ observer . ingestValue ( result , ingestConfig ) ;
194
+ }
195
+ }
196
+
107
197
/**
108
198
* Assigns a new function to the Computed Class for computing its value.
109
199
*
@@ -165,46 +255,19 @@ export class Computed<
165
255
public async compute (
166
256
config : ComputeConfigInterface = { }
167
257
) : Promise < ComputedValueType > {
168
- config = defineConfig ( config , {
169
- autodetect : this . config . autodetect ,
170
- } ) ;
171
-
172
- // Start auto tracking of Observers on which the computeFunction might depend
173
- if ( config . autodetect ) ComputedTracker . track ( ) ;
174
-
175
- const computedValue = this . computeFunction ( ) ;
176
-
177
- // Handle auto tracked Observers
178
- if ( config . autodetect ) {
179
- const foundDeps = ComputedTracker . getTrackedObservers ( ) ;
180
-
181
- // Clean up old dependencies
182
- this . deps . forEach ( ( observer ) => {
183
- if (
184
- ! foundDeps . includes ( observer ) &&
185
- ! this . hardCodedDeps . includes ( observer )
186
- ) {
187
- this . deps . delete ( observer ) ;
188
- observer . removeDependent ( this . observers [ 'value' ] ) ;
189
- }
190
- } ) ;
191
-
192
- // Make this Observer depend on the newly found dep Observers
193
- foundDeps . forEach ( ( observer ) => {
194
- if ( ! this . deps . has ( observer ) ) {
195
- this . deps . add ( observer ) ;
196
- observer . addDependent ( this . observers [ 'value' ] ) ;
197
- }
198
- } ) ;
258
+ if ( this . computeFunctionIsAsync ) {
259
+ return this . computeAsync ( config ) ;
260
+ }
261
+ else {
262
+ return this . computeSync ( config ) ;
199
263
}
200
-
201
- return computedValue ;
202
264
}
203
265
}
204
266
205
- export type ComputeFunctionType < ComputedValueType = any > = ( ) =>
206
- | ComputedValueType
207
- | Promise < ComputedValueType > ;
267
+ export type SyncComputeFunctionType < ComputedValueType = any > = ( ) => ComputedValueType ;
268
+ export type AsyncComputeFunctionType < ComputedValueType = any > = ( ) => Promise < ComputedValueType > ;
269
+
270
+ export type ComputeFunctionType < ComputedValueType = any > = SyncComputeFunctionType < ComputedValueType > | AsyncComputeFunctionType < ComputedValueType > ;
208
271
209
272
export interface CreateComputedConfigInterface < ComputedValueType = any >
210
273
extends StateConfigInterface {
0 commit comments