|
1 | | -/** @import { Derived, Effect, Source, Value } from '#client' */ |
| 1 | +/** @import { Derived, Effect, Reaction, Source, Value } from '#client' */ |
2 | 2 | import { |
3 | 3 | BLOCK_EFFECT, |
4 | 4 | BRANCH_EFFECT, |
@@ -342,31 +342,46 @@ export class Batch { |
342 | 342 | continue; |
343 | 343 | } |
344 | 344 |
|
| 345 | + /** @type {Source[]} */ |
| 346 | + const sources = []; |
| 347 | + |
345 | 348 | for (const [source, value] of this.current) { |
346 | 349 | if (batch.current.has(source)) { |
347 | | - if (is_earlier) { |
| 350 | + if (is_earlier && value !== batch.current.get(source)) { |
348 | 351 | // bring the value up to date |
349 | 352 | batch.current.set(source, value); |
350 | 353 | } else { |
351 | | - // later batch has more recent value, |
| 354 | + // same value or later batch has more recent value, |
352 | 355 | // no need to re-run these effects |
353 | 356 | continue; |
354 | 357 | } |
355 | 358 | } |
356 | 359 |
|
357 | | - mark_effects(source); |
| 360 | + sources.push(source); |
358 | 361 | } |
359 | 362 |
|
360 | | - if (queued_root_effects.length > 0) { |
361 | | - current_batch = batch; |
362 | | - batch.apply(); |
| 363 | + if (sources.length === 0) { |
| 364 | + continue; |
| 365 | + } |
363 | 366 |
|
364 | | - for (const root of queued_root_effects) { |
365 | | - batch.#traverse_effect_tree(root); |
| 367 | + // Re-run async/block effects that depend on distinct values changed in both batches |
| 368 | + const others = [...batch.current.keys()].filter((s) => !this.current.has(s)); |
| 369 | + if (others.length > 0) { |
| 370 | + for (const source of sources) { |
| 371 | + mark_effects(source, others); |
366 | 372 | } |
367 | 373 |
|
368 | | - queued_root_effects = []; |
369 | | - batch.deactivate(); |
| 374 | + if (queued_root_effects.length > 0) { |
| 375 | + current_batch = batch; |
| 376 | + batch.apply(); |
| 377 | + |
| 378 | + for (const root of queued_root_effects) { |
| 379 | + batch.#traverse_effect_tree(root); |
| 380 | + } |
| 381 | + |
| 382 | + queued_root_effects = []; |
| 383 | + batch.deactivate(); |
| 384 | + } |
370 | 385 | } |
371 | 386 | } |
372 | 387 |
|
@@ -621,24 +636,46 @@ function flush_queued_effects(effects) { |
621 | 636 |
|
622 | 637 | /** |
623 | 638 | * This is similar to `mark_reactions`, but it only marks async/block effects |
624 | | - * so that these can re-run after another batch has been committed |
| 639 | + * depending on `value` and at least one of the other `sources`, so that |
| 640 | + * these effects can re-run after another batch has been committed |
625 | 641 | * @param {Value} value |
| 642 | + * @param {Source[]} sources |
626 | 643 | */ |
627 | | -function mark_effects(value) { |
| 644 | +function mark_effects(value, sources) { |
628 | 645 | if (value.reactions !== null) { |
629 | 646 | for (const reaction of value.reactions) { |
630 | 647 | const flags = reaction.f; |
631 | 648 |
|
632 | 649 | if ((flags & DERIVED) !== 0) { |
633 | | - mark_effects(/** @type {Derived} */ (reaction)); |
634 | | - } else if ((flags & (ASYNC | BLOCK_EFFECT)) !== 0) { |
| 650 | + mark_effects(/** @type {Derived} */ (reaction), sources); |
| 651 | + } else if ((flags & (ASYNC | BLOCK_EFFECT)) !== 0 && depends_on(reaction, sources)) { |
635 | 652 | set_signal_status(reaction, DIRTY); |
636 | 653 | schedule_effect(/** @type {Effect} */ (reaction)); |
637 | 654 | } |
638 | 655 | } |
639 | 656 | } |
640 | 657 | } |
641 | 658 |
|
| 659 | +/** |
| 660 | + * @param {Reaction} reaction |
| 661 | + * @param {Source[]} sources |
| 662 | + */ |
| 663 | +function depends_on(reaction, sources) { |
| 664 | + if (reaction.deps !== null) { |
| 665 | + for (const dep of reaction.deps) { |
| 666 | + if (sources.includes(dep)) { |
| 667 | + return true; |
| 668 | + } |
| 669 | + |
| 670 | + if ((dep.f & DERIVED) !== 0 && depends_on(/** @type {Derived} */ (dep), sources)) { |
| 671 | + return true; |
| 672 | + } |
| 673 | + } |
| 674 | + } |
| 675 | + |
| 676 | + return false; |
| 677 | +} |
| 678 | + |
642 | 679 | /** |
643 | 680 | * @param {Effect} signal |
644 | 681 | * @returns {void} |
|
0 commit comments