Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/svelte/src/internal/client/reactivity/batch.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,12 @@ export class Batch {
this.previous.set(source, value);
}

// Don't save errors in `batch_values`, or they won't be thrown in `runtime.js#get`
if ((source.f & ERROR_VALUE) === 0) {
this.current.set(source, source.v);
batch_values?.set(source, source.v);
}

// The value is now the newest known value for this source, therefore remove it from batch_values
batch_values?.delete(source);
}

activate() {
Expand Down
14 changes: 13 additions & 1 deletion packages/svelte/src/internal/client/reactivity/deriveds.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,20 @@ export function update_derived(derived) {
return;
}

// During time traveling we don't want to reset the status so that
// traversal of the graph in the other batches still happens
if (batch_values !== null) {
batch_values.set(derived, derived.v);
// Delete the value as the current one is now the latest.
// Deleting instead of updating handles the case where a derived
// is subsequently indirectly updated in the same batch — without
// deleting here we would incorrectly get the old value from `batch_values`
// instead of recomputing it. The one drawback is that it's now a bit
// more inefficient to get the value of that derived again in the same batch,
// as it has to check is_dirty all the way up the graph all the time.
// TODO if that turns out to be a performance problem, we could try
// to save the current status of the derived in a map and restore it
// before leaving the batch.
batch_values.delete(derived);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels redundant, since we never do batch_values.set(derived, ...) in the first place. If I delete this and the other batch_values?.delete occurrence in batch.js, all the tests still pass.

So basically what this PR is doing is removing caching for deriveds altogether when time-travelling. It eliminates any problems around staleness, but it feels like quite a high cost

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We did set prior to this change, and a writable derived would end up in batch_values via internal_set.

Your adjustments should still make it better since we can now stop at the changed derived. Wonder if we can then also remove the && batch_values === null I added in is_dirty

} else {
var status = (derived.f & CONNECTED) === 0 ? MAYBE_DIRTY : CLEAN;
set_signal_status(derived, status);
Expand Down
36 changes: 7 additions & 29 deletions packages/svelte/src/internal/client/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,12 @@ export function is_dirty(reaction) {
}
}

if ((flags & CONNECTED) !== 0) {
if (
(flags & CONNECTED) !== 0 &&
// During time traveling we don't want to reset the status so that
// traversal of the graph in the other batches still happens
batch_values === null
) {
set_signal_status(reaction, CLEAN);
}
}
Expand Down Expand Up @@ -611,25 +616,15 @@ export function get(signal) {
} else if (is_derived) {
derived = /** @type {Derived} */ (signal);

var should_reconnect = is_updating_effect && effect_tracking() && (derived.f & CONNECTED) === 0;

if (batch_values?.has(derived)) {
// This happens as part of is_dirty normally, but we return early
// here so we need to do it separately
remove_marked_flag(derived);

if (should_reconnect) {
reconnect(derived);
}

return batch_values.get(derived);
}

if (is_dirty(derived)) {
update_derived(derived);
}

if (should_reconnect) {
if (is_updating_effect && effect_tracking() && (derived.f & CONNECTED) === 0) {
reconnect(derived);
}
} else if (batch_values?.has(signal)) {
Expand Down Expand Up @@ -662,23 +657,6 @@ function reconnect(derived) {
}
}

/**
* Removes the WAS_MARKED flag from the derived and its dependencies
* @param {Value} derived
*/
function remove_marked_flag(derived) {
// We cannot stop at the first non-marked derived because batch_values can
// cause "holes" of unmarked deriveds in an otherwise marked graph
if ((derived.f & DERIVED) === 0) return;

derived.f &= ~WAS_MARKED;

// Only deriveds with dependencies can be marked
for (const dep of /** @type {Value[]} */ (/** @type {Derived} */ (derived).deps)) {
remove_marked_flag(dep);
}
}

/** @param {Derived} derived */
function depends_on_old_values(derived) {
if (derived.v === UNINITIALIZED) return true; // we don't know, so assume the worst
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ export default test({

button?.click();
await tick();
// TODO this is wrong: it should be [5]
assert.deepEqual(logs, [4]);
assert.deepEqual(logs, [5]);

button?.click();
await tick();
// TODO this is wrong: it should be [5, 7]
assert.deepEqual(logs, [4, 7]);
assert.deepEqual(logs, [5, 7]);
}
});
Loading