chore: flatten `#process()` (#18190)

Realised that we can make the logic in `#process()` a little easier to
follow by early-returning if a batch is deferred (or blocked) after
processing, since nothing that happens after the `if` block applies in
that case
pull/18197/head
Rich Harris 2 months ago committed by GitHub
parent 9ffc13d7c3
commit af5b9724ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -344,26 +344,28 @@ export class Batch {
for (const [e, t] of this.#skipped_branches) {
reset_branch(e, t);
}
} else {
if (this.#pending === 0) {
batches.delete(this);
if (updates.length > 0) {
/** @type {Batch} */ (/** @type {unknown} */ (current_batch)).#process();
}
// clear effects. Those that are still needed will be rescheduled through unskipping the skipped branches.
this.#dirty_effects.clear();
this.#maybe_dirty_effects.clear();
return;
}
// append/remove branches
for (const fn of this.#commit_callbacks) fn(this);
this.#commit_callbacks.clear();
// clear effects. Those that are still needed will be rescheduled through unskipping the skipped branches.
this.#dirty_effects.clear();
this.#maybe_dirty_effects.clear();
previous_batch = this;
flush_queued_effects(render_effects);
flush_queued_effects(effects);
previous_batch = null;
// append/remove branches
for (const fn of this.#commit_callbacks) fn(this);
this.#commit_callbacks.clear();
this.#deferred?.resolve();
}
previous_batch = this;
flush_queued_effects(render_effects);
flush_queued_effects(effects);
previous_batch = null;
this.#deferred?.resolve();
var next_batch = /** @type {Batch | null} */ (/** @type {unknown} */ (current_batch));
@ -371,7 +373,7 @@ export class Batch {
// else we could start flushing a new batch and then, if it has pending work, rebase it right afterwards, which is wrong.
// In sync mode flushSync can cause #commit to wrongfully think that there needs to be a rebase, so we only do it in async mode
// TODO fix the underlying cause, otherwise this will likely regress when non-async mode is removed
if (async_mode_flag && !batches.has(this)) {
if (async_mode_flag && this.#pending === 0) {
this.#commit();
// Rebases can activate other batches or null it out, therefore restore the new one here
current_batch = next_batch;
@ -530,6 +532,8 @@ export class Batch {
}
#commit() {
batches.delete(this);
// If there are other pending batches, they now need to be 'rebased' —
// in other words, we re-run block/async effects with the newly
// committed state, unless the batch in question has a more

@ -0,0 +1,54 @@
import { tick } from 'svelte';
import { ok, test } from '../../test';
// Test that the store is unsubscribed from, even if it's not referenced once the store itself is set to null
export default test({
skip_no_async: true,
async test({ target, assert }) {
assert.htmlEqual(
target.innerHTML,
`<input type="number"> <p>0</p> <button>add watcher</button>`
);
target.querySelector('button')?.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`<input type="number"> <p>1</p> hello 1 <button>remove watcher</button>`
);
const input = target.querySelector('input');
ok(input);
input.stepUp();
input.dispatchEvent(new Event('input', { bubbles: true }));
await tick();
assert.htmlEqual(
target.innerHTML,
`<input type="number"> <p>2</p> hello 2 <button>remove watcher</button>`
);
target.querySelector('button')?.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`<input type="number"> <p>2</p> <button>add watcher</button>`
);
input.stepUp();
input.dispatchEvent(new Event('input', { bubbles: true }));
await tick();
assert.htmlEqual(
target.innerHTML,
`<input type="number"> <p>2</p> <button>add watcher</button>`
);
target.querySelector('button')?.click();
await tick();
assert.htmlEqual(
target.innerHTML,
`<input type="number"> <p>3</p> hello 3 <button>remove watcher</button>`
);
}
});

@ -0,0 +1,29 @@
<script>
import { writable, derived } from "svelte/store";
const obj = writable({ a: 1 });
let count = $state(0);
let watcherA = $state();
function watch (prop) {
return derived(obj, (o) => {
count++;
return o[prop];
});
}
</script>
<input type="number" bind:value={$obj.a}>
<p>{count}</p>
{#if watcherA}
<!-- make sure the presence of async work doesn't break the `legacy_updates` mechanism -->
{#if true}
{await 'hello'}
{/if}
{$watcherA}
<button on:click={() => watcherA = null}>remove watcher</button>
{:else}
<button on:click={() => watcherA = watch("a")}>add watcher</button>
{/if}
Loading…
Cancel
Save