Skip to content

Commit bf7581c

Browse files
committed
fix: prevent re-activated stale batches from break reactivity
1 parent 470d612 commit bf7581c

File tree

4 files changed

+38
-18
lines changed

4 files changed

+38
-18
lines changed

.changeset/rude-vans-retire.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: prevent re-activated stale batches from break reactivity

packages/svelte/src/internal/client/reactivity/batch.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,13 @@ export class Batch {
421421
}
422422

423423
static ensure() {
424+
if (current_batch !== null && !batches.has(current_batch)) {
425+
// A previously committed batch was reactivated via async `restore`.
426+
// Treat it as inactive so a new batch can be created to process updates.
427+
current_batch = null;
428+
batch_values = null;
429+
}
430+
424431
if (current_batch === null) {
425432
const batch = (current_batch = new Batch());
426433
batches.add(current_batch);

packages/svelte/tests/runtime-runes/samples/async-resolve-stale/_config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,9 @@ export default test({
2121
input.dispatchEvent(new Event('input', { bubbles: true }));
2222
await macrotask(6);
2323
assert.htmlEqual(target.innerHTML, '<input> 3 | 12');
24+
input.value = '';
25+
input.dispatchEvent(new Event('input', { bubbles: true }));
26+
await macrotask();
27+
assert.htmlEqual(target.innerHTML, '<input> 4 | ');
2428
}
2529
});

packages/svelte/tests/runtime-runes/samples/async-resolve-stale/main.svelte

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
<script>
22
let count = $state(0);
33
let value = $state('');
4-
let prev;
54
6-
function asd(v) {
7-
const r = Promise.withResolvers();
5+
let resolver;
86
9-
if (prev || v === '') {
10-
Promise.resolve().then(async () => {
11-
count++;
12-
r.resolve(v);
13-
await new Promise(r => setTimeout(r, 0));
14-
// TODO with a microtask like below it still throws a mutation error
15-
// await Promise.resolve();
16-
prev?.resolve();
17-
});
18-
} else {
19-
prev = Promise.withResolvers();
20-
prev.promise.then(() => {
21-
count++;
22-
r.resolve(v)
23-
});
7+
function asd(v) {
8+
let r = Promise.withResolvers();
9+
10+
function update_and_resolve(){
11+
count++;
12+
r.resolve(v);
2413
}
2514
15+
// make sure the second promise resolve before the first one
16+
if(resolver){
17+
new Promise(r => {
18+
setTimeout(r);
19+
}).then(update_and_resolve).then(()=>{
20+
setTimeout(()=>{
21+
resolver();
22+
resolver = null;
23+
});
24+
});
25+
}else if(v){
26+
resolver = update_and_resolve;
27+
}else{
28+
Promise.resolve().then(update_and_resolve);
29+
}
2630
return r.promise;
2731
}
2832

0 commit comments

Comments
 (0)