@@ -6,6 +6,7 @@ type SyncComputationInternal<T> = {
6
6
value ?: T ;
7
7
next ?: T ;
8
8
compute : ( ) => void ;
9
+ id : string ;
9
10
} ;
10
11
11
12
type GeneratorComputationInternal < T > = {
@@ -14,13 +15,16 @@ type GeneratorComputationInternal<T> = {
14
15
value ?: T ;
15
16
next ?: T ;
16
17
compute : ( ) => void ;
18
+ id : string ;
17
19
} ;
18
20
19
21
type ComputationInternal < T > =
20
22
| SyncComputationInternal < T >
21
23
| GeneratorComputationInternal < T > ;
22
24
23
- type DataInternal < T > = { type : "Stream" ; tag : "Data" ; value ?: T ; next ?: T } ;
25
+ type DataInternal < T > = {
26
+ type : "Stream" ; tag : "Data" ; value ?: T ; next ?: T , id : string
27
+ } ;
24
28
25
29
type StreamInternal < T > = DataInternal < T > | ComputationInternal < T > ;
26
30
@@ -70,7 +74,21 @@ const rootOfStream = new WeakMap<ComputationInternal<unknown>, Root>();
70
74
/**
71
75
* The computations that will need to rerun next tick
72
76
*/
73
- export let toRun = new Set < ComputationInternal < unknown > > ( ) ;
77
+ export let runningNextTick = new Set < ComputationInternal < unknown > > ( ) ;
78
+
79
+ /**
80
+ * The computations that are scheduled to run in the current propagation
81
+ */
82
+ export let runningThisTick = new Set < ComputationInternal < unknown > > ( ) ;
83
+
84
+ /**
85
+ * If a computation is nested within another computation
86
+ * and the parent is scheduled to run in this tick along with the child
87
+ * we only need to run the parent, as the child will automatically
88
+ * run when the parent compute runs, so we need to skip
89
+ * those child nodes to prevent double updates, that's what this set is for
90
+ */
91
+ export let allChildren = new Set < ComputationInternal < unknown > > ( ) ;
74
92
75
93
/**
76
94
* The direct dependencies of a stream, used in computeDependents
@@ -224,7 +242,7 @@ function computeDependents(stream: StreamInternal<unknown>) {
224
242
}
225
243
}
226
244
227
- toRun . add ( x ) ;
245
+ runningNextTick . add ( x ) ;
228
246
}
229
247
}
230
248
@@ -245,6 +263,9 @@ export function data<T>(
245
263
tag : "Data" ,
246
264
next : value ,
247
265
value,
266
+ get id ( ) {
267
+ return ids . get ( accessor ) !
268
+ }
248
269
} ;
249
270
250
271
let accessor = ( ...args : T [ ] | [ ( x ?: T ) => T ] ) => {
@@ -325,6 +346,9 @@ export function computation<T>(
325
346
streamsToResolve . add ( stream ) ;
326
347
}
327
348
} ,
349
+ get id ( ) {
350
+ return ids . get ( accessor ) !
351
+ }
328
352
} ;
329
353
330
354
// whatever active was when this was defined
@@ -365,15 +389,21 @@ export function computation<T>(
365
389
stream . compute ( ) ;
366
390
}
367
391
368
- return record ( stream , ( ) => {
392
+ const accessor = ( ) => {
393
+ if ( state === 'propagating' && runningThisTick . has ( stream ) ) {
394
+ // compute now
395
+ tickOne ( stream )
396
+ }
369
397
if ( active [ 0 ] ) {
370
398
xet ( dependents , stream , ( ) => new Set ( ) ) . add ( active [ 0 ] ) ;
371
399
372
400
return stream . next ! ;
373
401
} else {
374
402
return stream . value ! ;
375
403
}
376
- } ) ;
404
+ }
405
+
406
+ return record ( stream , accessor ) ;
377
407
}
378
408
379
409
// only defining these types as I couldn't
@@ -448,6 +478,9 @@ export function generator<T>(
448
478
}
449
479
} ) ;
450
480
} ,
481
+ get id ( ) {
482
+ return ids . get ( accessor ) !
483
+ }
451
484
} ;
452
485
453
486
async function iterate < T > ( it : StreamIterator < T > ) {
@@ -489,14 +522,19 @@ export function generator<T>(
489
522
490
523
stream . compute ( ) ;
491
524
492
- return record ( stream , ( ) => {
525
+ const accessor = ( ) => {
526
+ if ( state === 'propagating' && runningThisTick . has ( stream ) ) {
527
+ // compute now
528
+ tickOne ( stream )
529
+ }
493
530
if ( active [ 0 ] ) {
494
531
xet ( dependents , stream , ( ) => new Set ( ) ) . add ( active [ 0 ] ) ;
495
532
496
533
return stream . next ;
497
534
}
498
535
return stream . value ;
499
- } ) ;
536
+ }
537
+ return record ( stream , accessor ) ;
500
538
}
501
539
502
540
export function freeze ( f : VoidFunction ) : void {
@@ -548,7 +586,7 @@ export function root<T>(f: (dispose: VoidFunction) => T) {
548
586
cleanup ( ) ;
549
587
}
550
588
dependents . delete ( x ) ;
551
- toRun . delete ( x ) ;
589
+ runningNextTick . delete ( x ) ;
552
590
cleanups . delete ( x ) ;
553
591
parents . delete ( x ) ;
554
592
}
@@ -559,25 +597,57 @@ export function root<T>(f: (dispose: VoidFunction) => T) {
559
597
return out ;
560
598
}
561
599
600
+ function tickOne (
601
+ stream : ComputationInternal < unknown >
602
+ ) {
603
+ if ( ! runningThisTick . has ( stream ) ) {
604
+ // evalauted already by another compute
605
+ return ;
606
+ }
607
+ for ( let cleanupFn of xet ( cleanups , stream , ( ) => new Set ( ) ) ) {
608
+ cleanupFn ( ) ;
609
+ cleanups . get ( stream ) ! . delete ( cleanupFn ) ;
610
+ }
611
+
612
+ // if is a child, we only need to clean up
613
+ if ( allChildren . has ( stream ) ) {
614
+ return ;
615
+ } else {
616
+ let oldActive = active ;
617
+ active = [ ...( parents . get ( stream ) ?? [ ] ) ] ;
618
+
619
+ let oldActiveRoot = activeRoot ;
620
+ activeRoot = rootOfStream . get ( stream ) ?? null ;
621
+
622
+ stream . compute ( ) ;
623
+
624
+ active = oldActive ;
625
+ activeRoot = oldActiveRoot ;
626
+ stats . computations . evaluated ++ ;
627
+ }
628
+ runningThisTick . delete ( stream )
629
+ }
630
+
562
631
export function tick ( ) {
563
632
if ( state === "propagating" || state === "frozen" ) return ;
564
633
565
634
stats . ticks ++ ;
566
635
567
636
state = "propagating" ;
568
637
569
- let oldToRun = new Set ( toRun ) ;
638
+ runningThisTick = new Set ( runningNextTick ) ;
639
+
640
+ runningNextTick . clear ( ) ;
570
641
571
- toRun . clear ( ) ;
642
+ allChildren . clear ( )
572
643
573
- let allChildren = new Set < ComputationInternal < unknown > > ( ) ;
574
- for ( let x of oldToRun ) {
644
+ for ( let x of runningThisTick ) {
575
645
for ( let child of children . get ( x ) ?? [ ] ) {
576
646
// record the child exists
577
647
allChildren . add ( child ) ;
578
648
// add to the tick so we can run
579
649
// the clean up fns (at most once)
580
- oldToRun . add ( child ) ;
650
+ runningThisTick . add ( child ) ;
581
651
parents . delete ( child ) ;
582
652
streamsToResolve . delete ( child ) ;
583
653
// should we delete the dependents too?
@@ -588,28 +658,8 @@ export function tick() {
588
658
children . delete ( x ) ;
589
659
}
590
660
591
- for ( let f of oldToRun ) {
592
- for ( let cleanupFn of xet ( cleanups , f , ( ) => new Set ( ) ) ) {
593
- cleanupFn ( ) ;
594
- cleanups . get ( f ) ! . delete ( cleanupFn ) ;
595
- }
596
-
597
- // if is a child, we only need to clean up
598
- if ( allChildren . has ( f ) ) {
599
- continue ;
600
- } else {
601
- let oldActive = active ;
602
- active = [ ...( parents . get ( f ) ?? [ ] ) ] ;
603
-
604
- let oldActiveRoot = activeRoot ;
605
- activeRoot = rootOfStream . get ( f ) ?? null ;
606
-
607
- f . compute ( ) ;
608
-
609
- active = oldActive ;
610
- activeRoot = oldActiveRoot ;
611
- stats . computations . evaluated ++ ;
612
- }
661
+ for ( let stream of [ ...runningThisTick ] ) {
662
+ tickOne ( stream )
613
663
}
614
664
615
665
for ( let s of streamsToResolve ) {
@@ -635,6 +685,7 @@ export function tick() {
635
685
nextTicks . length = 0 ;
636
686
for ( let next of xs ) {
637
687
if ( runawayTicks > MAX_TICKS ) {
688
+ state = 'idle'
638
689
throw new RunawayTicks ( ) ;
639
690
}
640
691
next ( ) ;
@@ -659,6 +710,9 @@ export function sample<T>(signal: Signal<T>) {
659
710
return value ;
660
711
}
661
712
662
- export function id ( s : Signal < any > ) {
713
+ export function id ( s : Signal < any > , newId ?: string ) {
714
+ if ( newId != null ) {
715
+ ids . set ( s , newId )
716
+ }
663
717
return ids . get ( s ) ;
664
718
}
0 commit comments