Skip to content

Commit 14ba41e

Browse files
committed
avoid reawakening committed batches
1 parent bf90654 commit 14ba41e

File tree

5 files changed

+41
-20
lines changed

5 files changed

+41
-20
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/** @import { Effect, TemplateNode, Value } from '#client' */
2-
import { DESTROYED } from '#client/constants';
2+
import { DESTROYED, STALE_REACTION } from '#client/constants';
33
import { DEV } from 'esm-env';
44
import {
55
component_context,

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ let is_flushing = false;
7676
export let is_flushing_sync = false;
7777

7878
export class Batch {
79+
committed = false;
80+
7981
/**
8082
* The current values of any sources that are updated in this batch
8183
* They keys of this map are identical to `this.#previous`
@@ -399,6 +401,7 @@ export class Batch {
399401
batch_values = previous_batch_values;
400402
}
401403

404+
this.committed = true;
402405
batches.delete(this);
403406

404407
this.#deferred?.resolve();

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,17 @@ export function async_derived(fn, location) {
127127
// If this code is changed at some point, make sure to still access the then property
128128
// of fn() to read any signals it might access, so that we track them as dependencies.
129129
// We call `unset_context` to undo any `save` calls that happen inside `fn()`
130-
Promise.resolve(fn()).then(d.resolve, d.reject).then(unset_context);
130+
Promise.resolve(fn())
131+
.then(d.resolve, d.reject)
132+
.then(() => {
133+
if (batch === current_batch && batch.committed) {
134+
// if the batch was rejected as stale, we need to cleanup
135+
// after any `$.save(...)` calls inside `fn()`
136+
batch.deactivate();
137+
}
138+
139+
unset_context();
140+
});
131141
} catch (error) {
132142
d.reject(error);
133143
unset_context();

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)