chore: rebase batches after process, not during (#17900)

This is part of me trying to figure out #17162. It feels less confusing
to rebase other branches after the current batch has been processed,
rather than sort of doing it in the middle (which is an artifact of
historical constraints that no longer apply).

No test because it doesn't change any user-observable behaviour (but I
added a changeset just in case)

### Before submitting the PR, please make sure you do the following

- [x] It's really useful if your PR references an issue where it is
discussed ahead of time. In many cases, features are absent for a
reason. For large changes, please create an RFC:
https://github.com/sveltejs/rfcs
- [x] Prefix your PR title with `feat:`, `fix:`, `chore:`, or `docs:`.
- [x] This message body should clearly illustrate what problems it
solves.
- [ ] Ideally, include a test that fails without this PR but passes with
it.
- [x] If this PR changes code within `packages/svelte/src`, add a
changeset (`npx changeset`).

### Tests and linting

- [x] Run the tests with `pnpm test` and lint the project with `pnpm
lint`
always-populate-batch-values
Rich Harris 6 months ago committed by GitHub
parent 72cd247c33
commit 0e8f49b25f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
chore: rebase batches after process, not during

@ -249,6 +249,10 @@ export class Batch {
reset_branch(e, t); reset_branch(e, t);
} }
} else { } else {
if (this.#pending === 0) {
batches.delete(this);
}
// clear effects. Those that are still needed will be rescheduled through unskipping the skipped branches. // clear effects. Those that are still needed will be rescheduled through unskipping the skipped branches.
this.#dirty_effects.clear(); this.#dirty_effects.clear();
this.#maybe_dirty_effects.clear(); this.#maybe_dirty_effects.clear();
@ -262,10 +266,6 @@ export class Batch {
flush_queued_effects(effects); flush_queued_effects(effects);
previous_batch = null; previous_batch = null;
if (this.#pending === 0) {
this.#commit();
}
this.#deferred?.resolve(); this.#deferred?.resolve();
} }
@ -290,6 +290,10 @@ export class Batch {
next_batch.#process(); next_batch.#process();
} }
if (!batches.has(this)) {
this.#commit();
}
} }
/** /**
@ -433,74 +437,59 @@ export class Batch {
// in other words, we re-run block/async effects with the newly // in other words, we re-run block/async effects with the newly
// committed state, unless the batch in question has a more // committed state, unless the batch in question has a more
// recent value for a given source // recent value for a given source
if (batches.size > 1) { for (const batch of batches) {
this.previous.clear(); var is_earlier = batch.id < this.id;
var previous_batch = current_batch; /** @type {Source[]} */
var previous_batch_values = batch_values; var sources = [];
var is_earlier = true;
for (const [source, value] of this.current) {
for (const batch of batches) { if (batch.current.has(source)) {
if (batch === this) { if (is_earlier && value !== batch.current.get(source)) {
is_earlier = false; // bring the value up to date
continue; batch.current.set(source, value);
} else {
// same value or later batch has more recent value,
// no need to re-run these effects
continue;
}
} }
/** @type {Source[]} */ sources.push(source);
const sources = []; }
for (const [source, value] of this.current) {
if (batch.current.has(source)) {
if (is_earlier && value !== batch.current.get(source)) {
// bring the value up to date
batch.current.set(source, value);
} else {
// same value or later batch has more recent value,
// no need to re-run these effects
continue;
}
}
sources.push(source); if (sources.length === 0) {
} continue;
}
if (sources.length === 0) { // Re-run async/block effects that depend on distinct values changed in both batches
continue; var others = [...batch.current.keys()].filter((s) => !this.current.has(s));
} if (others.length > 0) {
batch.activate();
// Re-run async/block effects that depend on distinct values changed in both batches /** @type {Set<Value>} */
const others = [...batch.current.keys()].filter((s) => !this.current.has(s)); var marked = new Set();
if (others.length > 0) {
batch.activate();
/** @type {Set<Value>} */
const marked = new Set();
/** @type {Map<Reaction, boolean>} */
const checked = new Map();
for (const source of sources) {
mark_effects(source, others, marked, checked);
}
if (batch.#roots.length > 0) { /** @type {Map<Reaction, boolean>} */
batch.apply(); var checked = new Map();
for (const root of batch.#roots) { for (var source of sources) {
batch.#traverse(root, [], []); mark_effects(source, others, marked, checked);
} }
// TODO do we need to do anything with the dummy effect arrays? if (batch.#roots.length > 0) {
batch.apply();
for (var root of batch.#roots) {
batch.#traverse(root, [], []);
} }
batch.deactivate(); // TODO do we need to do anything with the dummy effect arrays?
} }
}
current_batch = previous_batch; batch.deactivate();
batch_values = previous_batch_values; }
} }
this.#skipped_branches.clear();
batches.delete(this);
} }
/** /**
@ -567,7 +556,10 @@ export class Batch {
} }
apply() { apply() {
if (!async_mode_flag || (!this.is_fork && batches.size === 1)) return; if (!async_mode_flag || (!this.is_fork && batches.size === 1)) {
batch_values = null;
return;
}
// if there are multiple batches, we are 'time travelling' — // if there are multiple batches, we are 'time travelling' —
// we need to override values with the ones in this batch... // we need to override values with the ones in this batch...

Loading…
Cancel
Save