@@ -89,9 +89,16 @@ export function set_current_effect(effect) {
89
89
current_effect = effect ;
90
90
}
91
91
92
- /** @type {null | import('#client').Value[] } */
93
- export let current_dependencies = null ;
94
- let current_dependencies_index = 0 ;
92
+ /**
93
+ * The dependencies of the reaction that is currently being executed. In many cases,
94
+ * the dependencies are unchanged between runs, and so this will be `null` unless
95
+ * and until a new dependency is accessed — we track this via `skipped_deps`
96
+ * @type {null | import('#client').Value[] }
97
+ */
98
+ export let new_deps = null ;
99
+
100
+ let skipped_deps = 0 ;
101
+
95
102
/**
96
103
* Tracks writes that the effect it's executed in doesn't listen to yet,
97
104
* so that the dependency can be added to the effect later on if it then reads it
@@ -296,63 +303,56 @@ function handle_error(error, effect, component_context) {
296
303
* @returns {V }
297
304
*/
298
305
export function update_reaction ( reaction ) {
299
- var previous_dependencies = current_dependencies ;
300
- var previous_dependencies_index = current_dependencies_index ;
306
+ var previous_deps = new_deps ;
307
+ var previous_skipped_deps = skipped_deps ;
301
308
var previous_untracked_writes = current_untracked_writes ;
302
309
var previous_reaction = current_reaction ;
303
310
var previous_skip_reaction = current_skip_reaction ;
304
311
305
- current_dependencies = /** @type {null | import('#client').Value[] } */ ( null ) ;
306
- current_dependencies_index = 0 ;
312
+ new_deps = /** @type {null | import('#client').Value[] } */ ( null ) ;
313
+ skipped_deps = 0 ;
307
314
current_untracked_writes = null ;
308
315
current_reaction = ( reaction . f & ( BRANCH_EFFECT | ROOT_EFFECT ) ) === 0 ? reaction : null ;
309
316
current_skip_reaction = ! is_flushing_effect && ( reaction . f & UNOWNED ) !== 0 ;
310
317
311
318
try {
312
319
var result = /** @type {Function } */ ( 0 , reaction . fn ) ( ) ;
313
- var dependencies = /** @type { import('#client').Value<unknown>[] } **/ ( reaction . deps ) ;
320
+ var deps = reaction . deps ;
314
321
315
- if ( current_dependencies !== null ) {
322
+ if ( new_deps !== null ) {
316
323
var dependency ;
317
324
var i ;
318
325
319
- if ( dependencies !== null ) {
320
- var deps_length = dependencies . length ;
321
-
326
+ if ( deps !== null ) {
322
327
/** All dependencies of the reaction, including those tracked on the previous run */
323
- var array =
324
- current_dependencies_index === 0
325
- ? current_dependencies
326
- : dependencies . slice ( 0 , current_dependencies_index ) . concat ( current_dependencies ) ;
328
+ var array = skipped_deps === 0 ? new_deps : deps . slice ( 0 , skipped_deps ) . concat ( new_deps ) ;
327
329
328
330
// If we have more than 16 elements in the array then use a Set for faster performance
329
331
// TODO: evaluate if we should always just use a Set or not here?
330
- var set =
331
- array . length > 16 && deps_length - current_dependencies_index > 1 ? new Set ( array ) : null ;
332
+ var set = array . length > 16 ? new Set ( array ) : null ;
332
333
333
- for ( i = current_dependencies_index ; i < deps_length ; i ++ ) {
334
- dependency = dependencies [ i ] ;
334
+ // Remove dependencies that should no longer be tracked
335
+ for ( i = skipped_deps ; i < deps . length ; i ++ ) {
336
+ dependency = deps [ i ] ;
335
337
336
338
if ( set !== null ? ! set . has ( dependency ) : ! array . includes ( dependency ) ) {
337
339
remove_reaction ( reaction , dependency ) ;
338
340
}
339
341
}
340
342
}
341
343
342
- if ( dependencies !== null && current_dependencies_index > 0 ) {
343
- dependencies . length = current_dependencies_index + current_dependencies . length ;
344
- for ( i = 0 ; i < current_dependencies . length ; i ++ ) {
345
- dependencies [ current_dependencies_index + i ] = current_dependencies [ i ] ;
344
+ if ( deps !== null && skipped_deps > 0 ) {
345
+ deps . length = skipped_deps + new_deps . length ;
346
+ for ( i = 0 ; i < new_deps . length ; i ++ ) {
347
+ deps [ skipped_deps + i ] = new_deps [ i ] ;
346
348
}
347
349
} else {
348
- reaction . deps = /** @type {import('#client').Value<V>[] } **/ (
349
- dependencies = current_dependencies
350
- ) ;
350
+ reaction . deps = deps = new_deps ;
351
351
}
352
352
353
353
if ( ! current_skip_reaction ) {
354
- for ( i = current_dependencies_index ; i < dependencies . length ; i ++ ) {
355
- dependency = dependencies [ i ] ;
354
+ for ( i = skipped_deps ; i < deps . length ; i ++ ) {
355
+ dependency = deps [ i ] ;
356
356
var reactions = dependency . reactions ;
357
357
358
358
if ( reactions === null ) {
@@ -365,15 +365,15 @@ export function update_reaction(reaction) {
365
365
}
366
366
}
367
367
}
368
- } else if ( dependencies !== null && current_dependencies_index < dependencies . length ) {
369
- remove_reactions ( reaction , current_dependencies_index ) ;
370
- dependencies . length = current_dependencies_index ;
368
+ } else if ( deps !== null && skipped_deps < deps . length ) {
369
+ remove_reactions ( reaction , skipped_deps ) ;
370
+ deps . length = skipped_deps ;
371
371
}
372
372
373
373
return result ;
374
374
} finally {
375
- current_dependencies = previous_dependencies ;
376
- current_dependencies_index = previous_dependencies_index ;
375
+ new_deps = previous_deps ;
376
+ skipped_deps = previous_skipped_deps ;
377
377
current_untracked_writes = previous_untracked_writes ;
378
378
current_reaction = previous_reaction ;
379
379
current_skip_reaction = previous_skip_reaction ;
@@ -755,7 +755,8 @@ export async function tick() {
755
755
* @returns {V }
756
756
*/
757
757
export function get ( signal ) {
758
- const flags = signal . f ;
758
+ var flags = signal . f ;
759
+
759
760
if ( ( flags & DESTROYED ) !== 0 ) {
760
761
return signal . v ;
761
762
}
@@ -766,26 +767,25 @@ export function get(signal) {
766
767
767
768
// Register the dependency on the current reaction signal.
768
769
if ( current_reaction !== null ) {
769
- const unowned = ( current_reaction . f & UNOWNED ) !== 0 ;
770
- const dependencies = current_reaction . deps ;
771
- if (
772
- current_dependencies === null &&
773
- dependencies !== null &&
774
- dependencies [ current_dependencies_index ] === signal &&
775
- ! ( unowned && current_effect !== null )
776
- ) {
777
- current_dependencies_index ++ ;
778
- } else if (
779
- dependencies === null ||
780
- current_dependencies_index === 0 ||
781
- dependencies [ current_dependencies_index - 1 ] !== signal
782
- ) {
783
- if ( current_dependencies === null ) {
784
- current_dependencies = [ signal ] ;
785
- } else if ( current_dependencies [ current_dependencies . length - 1 ] !== signal ) {
786
- current_dependencies . push ( signal ) ;
770
+ var deps = current_reaction . deps ;
771
+
772
+ // If the signal is accessing the same dependencies in the same
773
+ // order as it did last time, increment `skipped_deps`
774
+ // rather than updating `new_deps`, which creates GC cost
775
+ if ( new_deps === null && deps !== null && deps [ skipped_deps ] === signal ) {
776
+ skipped_deps ++ ;
777
+ }
778
+
779
+ // Otherwise, create or push to `new_deps`, but only if this
780
+ // dependency wasn't the last one that was accessed
781
+ else if ( deps === null || skipped_deps === 0 || deps [ skipped_deps - 1 ] !== signal ) {
782
+ if ( new_deps === null ) {
783
+ new_deps = [ signal ] ;
784
+ } else if ( new_deps [ new_deps . length - 1 ] !== signal ) {
785
+ new_deps . push ( signal ) ;
787
786
}
788
787
}
788
+
789
789
if (
790
790
current_untracked_writes !== null &&
791
791
current_effect !== null &&
0 commit comments