55 State ,
66 StateConfigInterface ,
77 StateIngestConfigInterface ,
8+ StateObserver ,
89} from '../state' ;
910import { Observer } from '../runtime' ;
1011import { ComputedTracker } from './computed.tracker' ;
@@ -15,8 +16,19 @@ export class Computed<
1516> extends State < ComputedValueType > {
1617 public config : ComputedConfigInterface ;
1718
19+ // Caches if the compute function is async
20+ private computeFunctionIsAsync ! : boolean ;
21+
1822 // 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+
2032 // All dependencies the Computed Class depends on (including hardCoded and automatically detected dependencies)
2133 public deps : Set < Observer > = new Set ( ) ;
2234 // Only hardCoded dependencies the Computed Class depends on
@@ -60,12 +72,13 @@ export class Computed<
6072 dependents : config . dependents ,
6173 }
6274 ) ;
75+ this . computeFunction = computeFunction ;
76+
6377 config = defineConfig ( config , {
6478 computedDeps : [ ] ,
65- autodetect : ! isAsyncFunction ( computeFunction ) ,
79+ autodetect : ! this . computeFunctionIsAsync ,
6680 } ) ;
6781 this . agileInstance = ( ) => agileInstance ;
68- this . computeFunction = computeFunction ;
6982 this . config = {
7083 autodetect : config . autodetect as any ,
7184 } ;
@@ -86,6 +99,64 @@ export class Computed<
8699 this . recompute ( { autodetect : config . autodetect , overwrite : true } ) ;
87100 }
88101
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+
89160 /**
90161 * Forces a recomputation of the cached value with the compute function.
91162 *
@@ -98,12 +169,31 @@ export class Computed<
98169 config = defineConfig ( config , {
99170 autodetect : false ,
100171 } ) ;
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+
104175 return this ;
105176 }
106177
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+
107197 /**
108198 * Assigns a new function to the Computed Class for computing its value.
109199 *
@@ -165,46 +255,19 @@ export class Computed<
165255 public async compute (
166256 config : ComputeConfigInterface = { }
167257 ) : 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 ) ;
199263 }
200-
201- return computedValue ;
202264 }
203265}
204266
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 > ;
208271
209272export interface CreateComputedConfigInterface < ComputedValueType = any >
210273 extends StateConfigInterface {
0 commit comments