()
| 650 | } |
| 651 | |
| 652 | #commit() { |
| 653 | // If there are other pending batches, they now need to be 'rebased' — |
| 654 | // in other words, we re-run block/async effects with the newly |
| 655 | // committed state, unless the batch in question has a more |
| 656 | // recent value for a given source |
| 657 | for (let batch = first_batch; batch !== null; batch = batch.#next) { |
| 658 | var is_earlier = batch.id < this.id; |
| 659 | |
| 660 | /** @type {Source[]} */ |
| 661 | var sources = []; |
| 662 | |
| 663 | for (const [source, [value, is_derived]] of this.current) { |
| 664 | if (batch.current.has(source)) { |
| 665 | var batch_value = /** @type {[any, boolean]} */ (batch.current.get(source))[0]; // faster than destructuring |
| 666 | |
| 667 | if (is_earlier && value !== batch_value) { |
| 668 | // bring the value up to date |
| 669 | batch.current.set(source, [value, is_derived]); |
| 670 | } else { |
| 671 | // same value or later batch has more recent value, |
| 672 | // no need to re-run these effects |
| 673 | continue; |
| 674 | } |
| 675 | } |
| 676 | |
| 677 | sources.push(source); |
| 678 | } |
| 679 | |
| 680 | if (is_earlier) { |
| 681 | // TODO do we need to restart these in some cases, instead of |
| 682 | // immediately resolving them? Likely not because of how this.apply() works. |
| 683 | for (const [effect, deferred] of this.async_deriveds) { |
| 684 | const d = batch.async_deriveds.get(effect); |
| 685 | if (d) deferred.promise.then(d.resolve).catch(d.reject); |
| 686 | } |
| 687 | } |
| 688 | |
| 689 | var current = [...batch.current.keys()].filter( |
| 690 | (source) => !(/** @type {[any, boolean]} */ (batch.current.get(source))[1]) |
| 691 | ); |
| 692 | |
| 693 | // If not started yet or no sources to update (which is e.g. possible for the very first batch) then bail |
| 694 | if (!batch.#started || current.length === 0) continue; |
| 695 | |
| 696 | // Re-run async/block effects that depend on distinct values changed in both batches (ignoring deriveds) |
| 697 | var others = current.filter((source) => !this.current.has(source)); |
| 698 | |
| 699 | if (others.length === 0) { |
| 700 | if (is_earlier) { |
| 701 | // this batch is now obsolete and can be discarded |
| 702 | batch.discard(); |
| 703 | } |
| 704 | } else if (sources.length > 0) { |
| 705 | // The microtask queue can contain the batch already scheduled to run right |
| 706 | // after this one is finished, so throwing the invariant would be wrong here. |
| 707 | if (DEV && !batch.#decrement_queued) { |
| 708 | invariant(batch.#roots.length === 0, 'Batch has scheduled roots'); |
| 709 | } |
no test coverage detected