async-another-try-pt-3
Simon Holthausen 5 days ago
parent 5dfac3270b
commit a6221946cf
No known key found for this signature in database

@ -23,7 +23,9 @@ import {
ERROR_VALUE, ERROR_VALUE,
MANAGED_EFFECT, MANAGED_EFFECT,
REACTION_RAN, REACTION_RAN,
ASYNC ASYNC,
EFFECT,
RENDER_EFFECT
} from './constants.js'; } from './constants.js';
import { invalidate, old_values } from './reactivity/sources.js'; import { invalidate, old_values } from './reactivity/sources.js';
import { import {
@ -504,10 +506,16 @@ export function update_effect(effect) {
// The effect ran with values that are not the latest ones (it saw its own batch's view). // The effect ran with values that are not the latest ones (it saw its own batch's view).
// Don't update its write version — instead remember it so that the batch can bring it // Don't update its write version — instead remember it so that the batch can bring it
// up to date on commit, and tell all subsequent batches that it may need to re-run in their view. // up to date on commit, and tell all subsequent batches that it may need to re-run in their view.
// Render/user effects have made that view visible (in the DOM, or through side effects), which their
// write version can't tell: their dependencies may be back to the values the effect last saw for real
// (e.g. a derived whose value didn't change), so they are dirty. Block/async effects are only maybe
// dirty, since their results are kept per batch, and they run during traversal: marking them dirty
// would re-run them (and restart async work) on every process of a pending batch.
var own = /** @type {Batch} */ (own_batch); var own = /** @type {Batch} */ (own_batch);
var status = (flags & (EFFECT | RENDER_EFFECT | MANAGED_EFFECT)) !== 0 ? DIRTY : MAYBE_DIRTY;
own.stale_effects.set(effect, write_version); own.stale_effects.set(effect, write_version);
for (var batch = own.next; batch !== null; batch = batch.next) { for (var batch = own.next; batch !== null; batch = batch.next) {
batch.add_dirty_reaction(effect, MAYBE_DIRTY); batch.add_dirty_reaction(effect, status);
} }
} }

@ -0,0 +1,35 @@
import { flushSync, tick } from 'svelte';
import { test } from '../../test';
// Two overlapping batches write to `b`. The first one commits first, running effects that read
// `sum` with its own (batch-local) view of `b`. The second one sets `b` back to its original
// value, so `sum` ends up where these effects last saw it outside of any batch-local view —
// they still need to re-run, since what they show is the first batch's view.
export default test({
mode: ['client'],
async test({ assert, target, instance }) {
for (let i = 0; i < 3; i++) {
instance.resolve_all();
await tick();
}
assert.htmlEqual(target.innerHTML, '<p>p0|0</p><em>slow0|key0</em>');
assert.deepEqual(instance.log, [0]);
instance.set_b(1);
flushSync();
instance.set_b(0);
// the first batch resolves and commits, the second one is still pending
instance.resolve_all();
await tick();
assert.htmlEqual(target.innerHTML, '<p>p0|1</p><em>slow0|key1</em>');
assert.deepEqual(instance.log, [0, 1]);
for (let i = 0; i < 3; i++) {
instance.resolve_all();
await tick();
}
assert.htmlEqual(target.innerHTML, '<p>p0|0</p><em>slow0|key0</em>');
assert.deepEqual(instance.log, [0, 1, 0]);
}
});

@ -0,0 +1,32 @@
<script>
let a = $state(0);
let b = $state(0);
let c = $state(0);
let sum = $derived(a + b);
const registry = [];
export const log = [];
$effect(() => {
log.push(sum);
});
let slow = $derived(await f('slow', c));
function f(tag, value) {
return new Promise((resolve) => registry.push(() => resolve(`${tag}${value}`)));
}
export function set_b(v) {
b = v;
}
export function resolve_all() {
for (const r of registry.splice(0)) r();
}
</script>
<p>{await f('p', a)}|{sum}</p>
{#key c}
<em>{slow}|{await f('key', b)}</em>
{/key}
Loading…
Cancel
Save