mirror of https://github.com/sveltejs/svelte
fix: preserve each items that are needed by pending batches (#17819)
This fixes a longstanding TODO with each blocks: currently, if any effects aren't used in the current batch at the moment of reconciliation, they are destroyed. Subsequent batches therefore end up recreating them. This is wasteful at the best of times, but if the effect contains any async work, that work has to be restarted. This PR fixes it by preserving any effects that correspond to the keys of pending batches. It _does_ mean that we need to iterate over each `keys` map for each pending batch in which an each block re-ran, but that is a rare scenario. This feels preferable to the alternative approaches.pull/17842/head
parent
791d5e332c
commit
7717ba01b4
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: preserve each items that are needed by pending batches
|
||||
@ -0,0 +1,65 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target }) {
|
||||
await tick();
|
||||
|
||||
const [add, shift] = target.querySelectorAll('button');
|
||||
|
||||
add.click();
|
||||
await tick();
|
||||
add.click();
|
||||
await tick();
|
||||
add.click();
|
||||
await tick();
|
||||
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>add</button>
|
||||
<button>shift</button>
|
||||
<p>1</p>
|
||||
`
|
||||
);
|
||||
|
||||
shift.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>add</button>
|
||||
<button>shift</button>
|
||||
<p>1</p>
|
||||
<p>2</p>
|
||||
`
|
||||
);
|
||||
|
||||
shift.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>add</button>
|
||||
<button>shift</button>
|
||||
<p>1</p>
|
||||
<p>2</p>
|
||||
<p>3</p>
|
||||
`
|
||||
);
|
||||
|
||||
shift.click();
|
||||
await tick();
|
||||
assert.htmlEqual(
|
||||
target.innerHTML,
|
||||
`
|
||||
<button>add</button>
|
||||
<button>shift</button>
|
||||
<p>1</p>
|
||||
<p>2</p>
|
||||
<p>3</p>
|
||||
<p>4</p>
|
||||
`
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,29 @@
|
||||
<script>
|
||||
let values = $state([1]);
|
||||
|
||||
const queue = [];
|
||||
|
||||
function push(v) {
|
||||
if (v === 1) return v;
|
||||
|
||||
const p = Promise.withResolvers();
|
||||
queue.push(() => p.resolve(v));
|
||||
return p.promise;
|
||||
}
|
||||
|
||||
function shift() {
|
||||
const fn = queue.shift();
|
||||
if (fn) fn();
|
||||
}
|
||||
|
||||
function addValue() {
|
||||
values = [...values, values.length + 1];
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={addValue}>add</button>
|
||||
<button onclick={shift}>shift</button>
|
||||
|
||||
{#each values as v}
|
||||
<p>{await push(v)}</p>
|
||||
{/each}
|
||||
Loading…
Reference in new issue