@@ -19,6 +19,16 @@ function _getCurrentObserver(): VoidFunction | undefined {
19
19
return context [ context . length - 1 ] ;
20
20
}
21
21
22
+ const UNSET = Symbol ( "UNSET" ) ;
23
+ const COMPUTING = Symbol ( "COMPUTING" ) ;
24
+ const ERRORED = Symbol ( "ERRORED" ) ;
25
+ const READY = Symbol ( "READY" ) ;
26
+
27
+ interface EffectNode {
28
+ error : unknown ;
29
+ state : symbol ;
30
+ }
31
+
22
32
/**
23
33
* Creates a new effect that will be executed immediately and whenever
24
34
* any of the signals it reads from change.
@@ -39,10 +49,22 @@ function _getCurrentObserver(): VoidFunction | undefined {
39
49
* @param fn The function to execute
40
50
*/
41
51
function $effect ( fn : VoidFunction ) : void {
52
+ const effectNode : EffectNode = {
53
+ error : null ,
54
+ state : UNSET
55
+ }
56
+
42
57
const execute = ( ) => {
58
+ if ( effectNode . state === COMPUTING ) {
59
+ throw new Error ( "Circular dependency detected" ) ;
60
+ }
61
+
43
62
context . push ( execute ) ;
44
63
try {
64
+ effectNode . state = COMPUTING ;
45
65
fn ( ) ;
66
+ effectNode . error = null ;
67
+ effectNode . state = READY ;
46
68
} finally {
47
69
context . pop ( ) ;
48
70
}
@@ -51,8 +73,22 @@ function $effect(fn: VoidFunction): void {
51
73
execute ( ) ;
52
74
}
53
75
76
+ interface ComputedNode < T > {
77
+ signal : Signal < T | undefined > ;
78
+ error : unknown ;
79
+ state : symbol ;
80
+ }
81
+
54
82
type ComputedFunction < T > = ( ) => T ;
55
83
84
+ function computedGetter < T > ( node : ComputedNode < T > ) {
85
+ if ( node . state === ERRORED ) {
86
+ throw node . error ;
87
+ }
88
+
89
+ return node . signal . readOnly as ReadOnlySignal < T > ;
90
+ }
91
+
56
92
/**
57
93
* Creates a new computed value that will be updated whenever the signals
58
94
* it reads from change. Returns a read-only signal that contains the
@@ -69,15 +105,29 @@ type ComputedFunction<T> = () => T;
69
105
* @param fn The function that returns the computed value.
70
106
*/
71
107
function $computed < T > ( fn : ComputedFunction < T > ) : ReadOnlySignal < T > {
72
- // The initial value is undefined, as it will be computed
73
- // when the effect runs for the first time
74
- const computedSignal : Signal < T | undefined > = $signal ( undefined ) ;
108
+ const computedNode : ComputedNode < T > = {
109
+ signal : $signal < T | undefined > ( undefined ) ,
110
+ error : null ,
111
+ state : UNSET
112
+ } ;
75
113
76
114
$effect ( ( ) => {
77
- computedSignal . value = fn ( ) ;
115
+ if ( computedNode . state === COMPUTING ) {
116
+ throw new Error ( "Circular dependency detected" ) ;
117
+ }
118
+
119
+ try {
120
+ computedNode . state = COMPUTING ;
121
+ computedNode . signal . value = fn ( ) ;
122
+ computedNode . error = null ;
123
+ computedNode . state = READY ;
124
+ } catch ( error ) {
125
+ computedNode . state = ERRORED ;
126
+ computedNode . error = error ;
127
+ }
78
128
} ) ;
79
129
80
- return computedSignal . readOnly as ReadOnlySignal < T > ;
130
+ return computedGetter ( computedNode ) ;
81
131
}
82
132
83
133
type StorageFn < T > = ( value : T ) => State < T > & { [ key : string ] : unknown } ;
0 commit comments