Skip to content

Commit 803c565

Browse files
authored
fix: properly invoke iterator.return() during reactivity loss check (#17966)
The logic was flipped - `iterator.return()` should be called when the iterator is abnormally ending, not when it's normally ending. Fixes #16610
1 parent 0adc22c commit 803c565

4 files changed

Lines changed: 69 additions & 2 deletions

File tree

.changeset/tasty-carrots-tie.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: properly invoke `iterator.return()` during reactivity loss check

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ export async function* for_await_track_reactivity_loss(iterable) {
209209
yield value;
210210
}
211211
} finally {
212-
// If the iterator had a normal completion and `return` is defined on the iterator, call it and return the value
213-
if (normal_completion && iterator.return !== undefined) {
212+
// If the iterator had an abrupt completion and `return` is defined on the iterator, call it and return the value
213+
if (!normal_completion && iterator.return !== undefined) {
214214
// eslint-disable-next-line no-unsafe-finally
215215
return /** @type {TReturn} */ ((await track_reactivity_loss(iterator.return()))().value);
216216
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { tick } from 'svelte';
2+
import { test } from '../../test';
3+
import { normalise_trace_logs } from '../../../helpers.js';
4+
5+
export default test({
6+
compileOptions: {
7+
dev: true
8+
},
9+
html: '<p>pending</p>',
10+
async test({ assert, target, warnings }) {
11+
await tick();
12+
13+
assert.htmlEqual(target.innerHTML, '<h1>number -> number -> number -> return -> ended</h1>');
14+
15+
assert.deepEqual(normalise_trace_logs(warnings), [
16+
{
17+
log: 'Detected reactivity loss when reading `values.length`. This happens when state is read in an async function after an earlier `await`'
18+
}
19+
]);
20+
}
21+
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<script>
2+
let values = $state([0, 1, 2]);
3+
4+
async function get_result() {
5+
const logs = [];
6+
7+
const iterator = {
8+
index: 0,
9+
async next() {
10+
if (this.index >= values.length) return { done: true };
11+
return { done: false, value: values[this.index++] };
12+
},
13+
async return() {
14+
logs.push('return');
15+
return { done: true };
16+
},
17+
[Symbol.asyncIterator]() {
18+
return this;
19+
}
20+
};
21+
22+
for await (const value of iterator) {
23+
logs.push('number');
24+
// read reactive state after async iterator await
25+
if (values.length === 3 && value === 2) {
26+
break;
27+
}
28+
}
29+
30+
logs.push('ended');
31+
return logs.join(' -> ');
32+
}
33+
</script>
34+
35+
<svelte:boundary>
36+
<h1>{await get_result()}</h1>
37+
38+
{#snippet pending()}
39+
<p>pending</p>
40+
{/snippet}
41+
</svelte:boundary>

0 commit comments

Comments
 (0)