Simon H 1 week ago committed by GitHub
commit 95ff2619e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: use right batch/branch on first run

@ -109,11 +109,14 @@ export class BranchManager {
}
for (const [b, k] of this.#batches) {
// Keep values for newer batches. Insertion order is not always chronological:
// an older batch can re-run after a newer one has already registered.
if (b.id > batch.id) continue;
this.#batches.delete(b);
if (b === batch) {
// keep values for newer batches
break;
continue;
}
const offscreen = this.#offscreen.get(k);

@ -59,7 +59,14 @@ export function flatten(blockers, sync, async, fn) {
return;
}
restore();
var batch = get_latest_async_batch(values);
if (batch) {
restore(false);
batch.activate();
batch.apply();
} else {
restore();
}
try {
fn(values);
@ -100,6 +107,24 @@ export function flatten(blockers, sync, async, fn) {
}
}
/**
* @param {Value[]} values
* @returns {Batch | null}
*/
function get_latest_async_batch(values) {
/** @type {Batch | null} */
var latest = null;
for (const value of values) {
var batch = /** @type {Value & { async_batch?: Batch }} */ (value).async_batch;
if (batch && (!latest || batch.id > latest.id)) {
latest = batch;
}
}
return latest;
}
/**
* @param {Blocker[]} blockers
* @param {(values: Value[]) => any} fn

@ -215,6 +215,7 @@ export function async_derived(fn, label, location) {
if (error === OBSOLETE) return;
batch.activate();
/** @type {Source<V> & { async_batch?: Batch }} */ (signal).async_batch = batch;
if (error) {
signal.f |= ERROR_VALUE;

@ -7,6 +7,7 @@ import type {
TransitionManager
} from '#client';
import type { Boundary } from '../dom/blocks/boundary';
import type { Batch } from './batch';
export interface Signal {
/** Flags bitmask */
@ -24,6 +25,8 @@ export interface Value<V = unknown> extends Signal {
rv: number;
/** The latest value for this signal */
v: V;
/** The batch in which an async derived most recently resolved */
async_batch?: Batch; // TODO if this is only set a few times this might mess with perf (object shape etc)
// dev-only
/** A label (e.g. the `foo` in `let foo = $state(...)`) used for `$inspect.trace()` */

@ -0,0 +1,20 @@
import { tick } from 'svelte';
import { test } from '../../test';
export default test({
async test({ assert, target }) {
const [increment, pop] = target.querySelectorAll('button');
increment.click();
await tick();
increment.click();
await tick();
pop.click();
await tick();
assert.htmlEqual(target.innerHTML, `<button>increment</button> <button>pop</button> 2 2 1`);
pop.click();
await tick();
assert.htmlEqual(target.innerHTML, `<button>increment</button> <button>pop</button> 2 2 1`);
}
});

@ -0,0 +1,26 @@
<script>
let count = $state(0);
let other = $state(0);
const queue = [];
function push(v) {
return new Promise((r,e) => queue.push(() => v === 1 ? e(v) : r(v)));
}
</script>
<button onclick={() => {
if (count === 0) {
other++;
count++;
} else {
count++
}
}}>increment</button>
<button onclick={() => queue.pop()?.()}>pop</button>
{#if count > 0}
<svelte:boundary>
{await push(count)} {count} {other}
{#snippet failed()}boom{/snippet}
</svelte:boundary>
{/if}
Loading…
Cancel
Save