@@ -28,7 +28,7 @@ import * as e from '../errors.js';
28
28
import { flush_tasks } from '../dom/task.js' ;
29
29
import { DEV } from 'esm-env' ;
30
30
import { invoke_error_boundary } from '../error-handling.js' ;
31
- import { old_values } from './sources.js' ;
31
+ import { mark_reactions , old_values } from './sources.js' ;
32
32
import { unlink_effect } from './effects.js' ;
33
33
import { unset_context } from './async.js' ;
34
34
@@ -70,13 +70,15 @@ let last_scheduled_effect = null;
70
70
71
71
let is_flushing = false ;
72
72
73
+ let flushing_sync = false ;
74
+
73
75
export class Batch {
74
76
/**
75
77
* The current values of any sources that are updated in this batch
76
78
* They keys of this map are identical to `this.#previous`
77
79
* @type {Map<Source, any> }
78
80
*/
79
- # current = new Map ( ) ;
81
+ current = new Map ( ) ;
80
82
81
83
/**
82
84
* The values of any sources that are updated in this batch _before_ those updates took place.
@@ -156,7 +158,7 @@ export class Batch {
156
158
*
157
159
* @param {Effect[] } root_effects
158
160
*/
159
- # process( root_effects ) {
161
+ process ( root_effects ) {
160
162
queued_root_effects = [ ] ;
161
163
162
164
/** @type {Map<Source, { v: unknown, wv: number }> | null } */
@@ -169,7 +171,7 @@ export class Batch {
169
171
current_values = new Map ( ) ;
170
172
batch_deriveds = new Map ( ) ;
171
173
172
- for ( const [ source , current ] of this . # current) {
174
+ for ( const [ source , current ] of this . current ) {
173
175
current_values . set ( source , { v : source . v , wv : source . wv } ) ;
174
176
source . v = current ;
175
177
}
@@ -300,7 +302,7 @@ export class Batch {
300
302
this . #previous. set ( source , value ) ;
301
303
}
302
304
303
- this . # current. set ( source , source . v ) ;
305
+ this . current . set ( source , source . v ) ;
304
306
}
305
307
306
308
activate ( ) {
@@ -346,49 +348,7 @@ export class Batch {
346
348
}
347
349
348
350
flush_effects ( ) {
349
- var was_updating_effect = is_updating_effect ;
350
- is_flushing = true ;
351
-
352
- try {
353
- var flush_count = 0 ;
354
- set_is_updating_effect ( true ) ;
355
-
356
- while ( queued_root_effects . length > 0 ) {
357
- if ( flush_count ++ > 1000 ) {
358
- if ( DEV ) {
359
- var updates = new Map ( ) ;
360
-
361
- for ( const source of this . #current. keys ( ) ) {
362
- for ( const [ stack , update ] of source . updated ?? [ ] ) {
363
- var entry = updates . get ( stack ) ;
364
-
365
- if ( ! entry ) {
366
- entry = { error : update . error , count : 0 } ;
367
- updates . set ( stack , entry ) ;
368
- }
369
-
370
- entry . count += update . count ;
371
- }
372
- }
373
-
374
- for ( const update of updates . values ( ) ) {
375
- // eslint-disable-next-line no-console
376
- console . error ( update . error ) ;
377
- }
378
- }
379
-
380
- infinite_loop_guard ( ) ;
381
- }
382
-
383
- this . #process( queued_root_effects ) ;
384
- old_values . clear ( ) ;
385
- }
386
- } finally {
387
- is_flushing = false ;
388
- set_is_updating_effect ( was_updating_effect ) ;
389
-
390
- last_scheduled_effect = null ;
391
- }
351
+ flush_effects ( ) ;
392
352
}
393
353
394
354
/**
@@ -412,19 +372,8 @@ export class Batch {
412
372
this . #pending -= 1 ;
413
373
414
374
if ( this . #pending === 0 ) {
415
- for ( const e of this . #render_effects) {
416
- set_signal_status ( e , DIRTY ) ;
417
- schedule_effect ( e ) ;
418
- }
419
-
420
- for ( const e of this . #effects) {
421
- set_signal_status ( e , DIRTY ) ;
422
- schedule_effect ( e ) ;
423
- }
424
-
425
- for ( const e of this . #block_effects) {
426
- set_signal_status ( e , DIRTY ) ;
427
- schedule_effect ( e ) ;
375
+ for ( const source of this . current . keys ( ) ) {
376
+ mark_reactions ( source , DIRTY , false ) ;
428
377
}
429
378
430
379
this . #render_effects = [ ] ;
@@ -487,32 +436,88 @@ export function flushSync(fn) {
487
436
e . flush_sync_in_effect ( ) ;
488
437
}
489
438
490
- var result ;
439
+ var prev_flushing_sync = flushing_sync ;
440
+ flushing_sync = true ;
491
441
492
- const batch = Batch . ensure ( false ) ;
442
+ try {
443
+ var result ;
493
444
494
- if ( fn ) {
495
- batch . flush_effects ( ) ;
445
+ const batch = Batch . ensure ( false ) ;
496
446
497
- result = fn ( ) ;
498
- }
447
+ if ( fn ) {
448
+ batch . flush_effects ( ) ;
499
449
500
- while ( true ) {
501
- flush_tasks ( ) ;
450
+ result = fn ( ) ;
451
+ }
452
+
453
+ while ( true ) {
454
+ flush_tasks ( ) ;
455
+
456
+ if ( queued_root_effects . length === 0 ) {
457
+ // TODO this might need adjustment
458
+ if ( batch === current_batch ) {
459
+ batch . flush ( ) ;
460
+ }
461
+
462
+ // this would be reset in `batch.flush_effects()` but since we are early returning here,
463
+ // we need to reset it here as well in case the first time there's 0 queued root effects
464
+ last_scheduled_effect = null ;
502
465
503
- if ( queued_root_effects . length === 0 ) {
504
- if ( batch === current_batch ) {
505
- batch . flush ( ) ;
466
+ return /** @type {T } */ ( result ) ;
506
467
}
507
468
508
- // this would be reset in `batch.flush_effects()` but since we are early returning here,
509
- // we need to reset it here as well in case the first time there's 0 queued root effects
510
- last_scheduled_effect = null ;
469
+ batch . flush_effects ( ) ;
470
+ }
471
+ } finally {
472
+ flushing_sync = prev_flushing_sync ;
473
+ }
474
+ }
511
475
512
- return /** @type {T } */ ( result ) ;
476
+ function flush_effects ( ) {
477
+ var was_updating_effect = is_updating_effect ;
478
+ is_flushing = true ;
479
+
480
+ try {
481
+ var flush_count = 0 ;
482
+ var batch = /** @type {Batch } */ ( current_batch ) ;
483
+ set_is_updating_effect ( true ) ;
484
+
485
+ while ( queued_root_effects . length > 0 ) {
486
+ if ( flush_count ++ > 1000 ) {
487
+ if ( DEV ) {
488
+ var updates = new Map ( ) ;
489
+
490
+ for ( const source of batch . current . keys ( ) ) {
491
+ for ( const [ stack , update ] of source . updated ?? [ ] ) {
492
+ var entry = updates . get ( stack ) ;
493
+
494
+ if ( ! entry ) {
495
+ entry = { error : update . error , count : 0 } ;
496
+ updates . set ( stack , entry ) ;
497
+ }
498
+
499
+ entry . count += update . count ;
500
+ }
501
+ }
502
+
503
+ for ( const update of updates . values ( ) ) {
504
+ // eslint-disable-next-line no-console
505
+ console . error ( update . error ) ;
506
+ }
507
+ }
508
+
509
+ infinite_loop_guard ( ) ;
510
+ }
511
+
512
+ batch = /** @type {Batch } */ ( current_batch ) ;
513
+ batch . process ( queued_root_effects ) ;
514
+ old_values . clear ( ) ;
513
515
}
516
+ } finally {
517
+ is_flushing = false ;
518
+ set_is_updating_effect ( was_updating_effect ) ;
514
519
515
- batch . flush_effects ( ) ;
520
+ last_scheduled_effect = null ;
516
521
}
517
522
}
518
523
@@ -545,6 +550,7 @@ function flush_queued_effects(effects) {
545
550
if ( ( effect . f & ( DESTROYED | INERT ) ) === 0 ) {
546
551
if ( is_dirty ( effect ) ) {
547
552
var wv = write_version ;
553
+ var current_size = /** @type {Batch } */ ( current_batch ) . current . size ;
548
554
549
555
update_effect ( effect ) ;
550
556
@@ -568,6 +574,22 @@ function flush_queued_effects(effects) {
568
574
// if state is written in a user effect, abort and re-schedule, lest we run
569
575
// effects that should be removed as a result of the state change
570
576
if ( write_version > wv && ( effect . f & USER_EFFECT ) !== 0 ) {
577
+ // Work needs to happen in a separate batch, else prior sources would be mixed with
578
+ // newly updated sources, which could lead to infinite loops when effects run over and over again.
579
+ // We need to bring over the just written sources though to correctly mark the right reactions as dirty.
580
+ var old_batch = /** @type {Batch } */ ( current_batch ) ;
581
+ batches . delete ( old_batch ) ;
582
+ current_batch = null ;
583
+ var new_batch = Batch . ensure ( ! flushing_sync ) ;
584
+ var current_idx = 0 ;
585
+ // We're taking advantage of the spec here which says that entries in a Map are traversed by insertion order
586
+ for ( const source of old_batch . current ) {
587
+ if ( current_idx >= current_size ) {
588
+ new_batch . capture ( source [ 0 ] , source [ 1 ] ) ;
589
+ }
590
+ current_idx ++ ;
591
+ }
592
+ i ++ ;
571
593
break ;
572
594
}
573
595
}
0 commit comments