11import * as alien from 'alien-signals' ;
22
3- const WATCHER_TRACK_ID = - 1 ;
3+ const WATCHER_PLACEHOLDER = Symbol ( 'watcher' ) as any ;
44
55export namespace Signal {
6- export const untrack = alien . untrack ;
7-
8- export function batch < T > ( fn : ( ) => T ) : T {
9- alien . startBatch ( ) ;
6+ const { drainQueuedEffects, endTrack, isDirty, link, propagate, shallowPropagate, startTrack} =
7+ alien . createSystem ( {
8+ isComputed ( sub ) : sub is Computed {
9+ return sub instanceof Computed ;
10+ } ,
11+ isEffect ( sub ) : sub is subtle . Watcher {
12+ return sub instanceof subtle . Watcher ;
13+ } ,
14+ notifyEffect ( watcher ) {
15+ watcher . notify ( ) ;
16+ } ,
17+ updateComputed ( computed ) {
18+ return computed . update ( ) ;
19+ } ,
20+ } ) ;
21+
22+ let activeSub : alien . Subscriber | undefined ;
23+
24+ export function untrack < T > ( fn : ( ) => T ) {
25+ const prevSub = activeSub ;
26+ activeSub = undefined ;
1027 try {
1128 return fn ( ) ;
1229 } finally {
13- alien . endBatch ( ) ;
30+ activeSub = prevSub ;
1431 }
1532 }
1633
17- export class State < T = any > extends alien . Signal < T > {
34+ export class State < T = any > implements alien . Dependency {
35+ subs : alien . Link | undefined = undefined ;
36+ subsTail : alien . Link | undefined = undefined ;
1837 watchCount = 0 ;
1938
2039 constructor (
21- value : T ,
40+ private currentValue : T ,
2241 private options ?: Options < T > ,
2342 ) {
24- super ( value ) ;
2543 if ( options && options . equals ) {
2644 this . equals = options . equals ;
2745 }
@@ -44,41 +62,47 @@ export namespace Signal {
4462 }
4563
4664 get ( ) {
47- if ( alien . activeTrackId === WATCHER_TRACK_ID ) {
65+ if ( activeSub === WATCHER_PLACEHOLDER ) {
4866 throw new Error ( 'Cannot read from state inside watcher' ) ;
4967 }
50- const lastSub = this . subsTail ;
51- const value = super . get ( ) ;
52- const newSub = this . subsTail ;
53- if ( lastSub !== newSub && newSub instanceof Computed && newSub . watchCount ) {
54- this . onWatched ( ) ;
68+ if ( activeSub !== undefined && link ( this , activeSub ) ) {
69+ const newSub = this . subsTail ! ;
70+ if ( newSub instanceof Computed && newSub . watchCount ) {
71+ this . onWatched ( ) ;
72+ }
5573 }
56- return value ;
74+ return this . currentValue ;
5775 }
5876
5977 set ( value : T ) : void {
60- if ( alien . activeTrackId === WATCHER_TRACK_ID ) {
78+ if ( activeSub === WATCHER_PLACEHOLDER ) {
6179 throw new Error ( 'Cannot write to state inside watcher' ) ;
6280 }
6381 if ( ! this . equals ( this . currentValue , value ) ) {
6482 this . currentValue = value ;
6583 const subs = this . subs ;
6684 if ( subs !== undefined ) {
67- alien . propagate ( subs ) ;
85+ propagate ( subs ) ;
86+ drainQueuedEffects ( ) ;
6887 }
6988 }
7089 }
7190 }
7291
73- export class Computed < T = any > extends alien . Computed < T > {
92+ export class Computed < T = any > implements alien . Dependency , alien . Subscriber {
93+ subs : alien . Link | undefined = undefined ;
94+ subsTail : alien . Link | undefined = undefined ;
95+ deps : alien . Link | undefined = undefined ;
96+ depsTail : alien . Link | undefined = undefined ;
97+ flags = alien . SubscriberFlags . Dirty ;
7498 isError = true ;
7599 watchCount = 0 ;
100+ currentValue : T | undefined = undefined ;
76101
77102 constructor (
78- getter : ( ) => T ,
103+ private getter : ( ) => T ,
79104 private options ?: Options < T > ,
80105 ) {
81- super ( getter ) ;
82106 if ( options && options . equals ) {
83107 this . equals = options . equals ;
84108 }
@@ -116,61 +140,71 @@ export namespace Signal {
116140 if ( this . flags & alien . SubscriberFlags . Tracking ) {
117141 throw new Error ( 'Cycles detected' ) ;
118142 }
119- if ( alien . activeTrackId === WATCHER_TRACK_ID ) {
143+ if ( activeSub === WATCHER_PLACEHOLDER ) {
120144 throw new Error ( 'Cannot read from computed inside watcher' ) ;
121145 }
122- const lastSub = this . subsTail ;
123- const value = super . get ( ) ;
124- const newSub = this . subsTail ;
125- if ( lastSub !== newSub && newSub instanceof Computed && newSub . watchCount ) {
126- this . onWatched ( ) ;
146+
147+ if ( isDirty ( this , this . flags ) ) {
148+ if ( this . update ( ) ) {
149+ const subs = this . subs ;
150+ if ( subs !== undefined ) {
151+ shallowPropagate ( subs ) ;
152+ }
153+ }
127154 }
155+ if ( activeSub !== undefined && link ( this , activeSub ) ) {
156+ const newSub = this . subsTail ! ;
157+ if ( newSub instanceof Computed && newSub . watchCount ) {
158+ this . onWatched ( ) ;
159+ }
160+ }
161+
128162 if ( this . isError ) {
129- throw value ;
163+ throw this . currentValue ;
130164 }
131- return value ;
165+
166+ return this . currentValue ;
132167 }
133168
134169 update ( ) : boolean {
135- const prevSub = alien . activeSub ;
136- const prevTrackId = alien . activeTrackId ;
137- alien . setActiveSub ( this , alien . nextTrackId ( ) ) ;
138- alien . startTrack ( this ) ;
170+ const prevSub = activeSub ;
171+ activeSub = this ;
172+ startTrack ( this ) ;
173+ const oldValue = this . currentValue ;
139174 try {
140- const oldValue = this . currentValue ;
141- const newValue = this . getter ( oldValue ) ;
175+ const newValue = this . getter ( ) ;
142176 if ( this . isError || ! this . equals ( oldValue ! , newValue ) ) {
143177 this . isError = false ;
144178 this . currentValue = newValue ;
145179 return true ;
146180 }
147181 return false ;
148182 } catch ( err ) {
149- this . isError = true ;
150- this . currentValue = err as any ;
151- return true ;
183+ if ( ! this . isError || ! this . equals ( oldValue ! , err as any ) ) {
184+ this . isError = true ;
185+ this . currentValue = err as any ;
186+ return true ;
187+ }
188+ return false ;
152189 } finally {
153- let removeDeps ! : AnySignal [ ] ;
154190 if ( this . watchCount ) {
155- removeDeps = [ ] ;
156- let link = this . depsTail ? this . depsTail . nextDep : this . deps ;
157- while ( link ) {
191+ for (
192+ let link = this . depsTail !== undefined ? this . depsTail . nextDep : this . deps ;
193+ link !== undefined ;
194+ link = link . nextDep
195+ ) {
158196 const dep = link . dep as AnySignal ;
159- removeDeps . push ( dep ) ;
160- link = link . nextDep ;
197+ dep . onUnwatched ( ) ;
161198 }
162199 }
163- alien . setActiveSub ( prevSub , prevTrackId ) ;
164- alien . endTrack ( this ) ;
200+
201+ activeSub = prevSub ;
202+ endTrack ( this ) ;
203+
165204 if ( this . watchCount ) {
166- for ( const dep of removeDeps ) {
167- dep . onUnwatched ( ) ;
168- }
169- let link = this . deps ;
170- while ( link ) {
205+ for ( let link = this . deps ; link !== undefined ; link = link . nextDep ) {
171206 const dep = link . dep as AnySignal ;
172207 dep . onWatched ( ) ;
173- link = link . nextDep ;
174208 }
175209 }
176210 }
@@ -180,56 +214,53 @@ export namespace Signal {
180214 // eslint-disable-next-line @typescript-eslint/no-explicit-any
181215 type AnySignal < T = any > = State < T > | Computed < T > ;
182216
183- const PENDING = 1 << 5 ;
184-
185217 export namespace subtle {
186- export class Watcher extends alien . Effect {
187- constructor ( fn : ( ) => void ) {
188- super ( fn ) ;
189- }
218+ export class Watcher implements alien . Subscriber {
219+ deps : alien . Link | undefined = undefined ;
220+ depsTail : alien . Link | undefined = undefined ;
221+ flags = alien . SubscriberFlags . None ;
222+
223+ constructor ( private fn : ( ) => void ) { }
190224
191225 notify ( ) {
192- let flags = this . flags ;
193- if ( flags & alien . SubscriberFlags . Dirty ) {
226+ if ( this . flags & alien . SubscriberFlags . Dirty ) {
194227 this . run ( ) ;
195- } else if ( flags & alien . SubscriberFlags . ToCheckDirty ) {
196- this . flags = PENDING ;
197228 }
198229 }
199230
200231 run ( ) {
201232 this . flags = alien . SubscriberFlags . None ;
202- const prevSub = alien . activeSub ;
203- const prevTrackId = alien . activeTrackId ;
204- alien . setActiveSub ( undefined , WATCHER_TRACK_ID ) ;
233+ const prevSub = activeSub ;
234+ activeSub = WATCHER_PLACEHOLDER ;
205235 try {
206236 this . fn ( ) ;
207237 } finally {
208- alien . setActiveSub ( prevSub , prevTrackId ) ;
238+ activeSub = prevSub ;
209239 }
210240 }
211241
212242 watch ( ...signals : AnySignal [ ] ) : void {
213243 for ( const signal of signals ) {
214- alien . link ( signal , this ) ;
215- signal . onWatched ( ) ;
244+ if ( link ( signal , this ) ) {
245+ signal . onWatched ( ) ;
246+ }
216247 }
217248 this . flags = alien . SubscriberFlags . None ;
218249 }
219250
220251 unwatch ( ...signals : AnySignal [ ] ) : void {
221- alien . startTrack ( this ) ;
252+ startTrack ( this ) ;
222253 let dep = this . deps ;
223254 while ( dep ) {
224255 if ( ! signals . includes ( dep . dep as AnySignal ) ) {
225- alien . link ( dep . dep , this ) ;
256+ link ( dep . dep , this ) ;
226257 }
227258 dep = dep . nextDep ;
228259 }
229260 for ( const signal of signals ) {
230- signal . onUnwatched ( ) ;
261+ signal . onUnwatched ( ) ; // TODO: check if it's watched
231262 }
232- alien . endTrack ( this ) ;
263+ endTrack ( this ) ;
233264 }
234265
235266 getPending ( ) {
0 commit comments