mirror of https://github.com/sveltejs/svelte
chore: emit `await_reactivity_loss` in `for await` loops (#16521)
* chore: emit `await_reactivity_loss` in `for await` loops * oops * fix lint * fix import order * input is an iterable, not an iterator * handle non-iterables * add test * typescript. shrug --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>pull/16566/head
parent
bbd0b1ed87
commit
b181c45484
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
chore: emit `await_reactivity_loss` in `for await` loops
|
@ -0,0 +1,20 @@
|
||||
/** @import { Expression, ForOfStatement, Pattern, Statement, VariableDeclaration } from 'estree' */
|
||||
/** @import { ComponentContext } from '../types' */
|
||||
import * as b from '#compiler/builders';
|
||||
import { dev, is_ignored } from '../../../../state.js';
|
||||
|
||||
/**
|
||||
* @param {ForOfStatement} node
|
||||
* @param {ComponentContext} context
|
||||
*/
|
||||
export function ForOfStatement(node, context) {
|
||||
if (node.await && dev && !is_ignored(node, 'await_reactivity_loss')) {
|
||||
const left = /** @type {VariableDeclaration | Pattern} */ (context.visit(node.left));
|
||||
const argument = /** @type {Expression} */ (context.visit(node.right));
|
||||
const body = /** @type {Statement} */ (context.visit(node.body));
|
||||
const right = b.call('$.for_await_track_reactivity_loss', argument);
|
||||
return b.for_of(left, right, body, true);
|
||||
}
|
||||
|
||||
context.next();
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { tick } from 'svelte';
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
compileOptions: {
|
||||
dev: true
|
||||
},
|
||||
|
||||
html: `<button>a</button><button>b</button><p>pending</p>`,
|
||||
|
||||
async test({ assert, target, warnings }) {
|
||||
await tick();
|
||||
assert.htmlEqual(target.innerHTML, '<button>a</button><button>b</button><h1>3</h1>');
|
||||
|
||||
assert.equal(
|
||||
warnings[0],
|
||||
'Detected reactivity loss when reading `values[1]`. This happens when state is read in an async function after an earlier `await`'
|
||||
);
|
||||
|
||||
assert.equal(warnings[1].name, 'TracedAtError');
|
||||
|
||||
assert.equal(warnings.length, 2);
|
||||
}
|
||||
});
|
@ -0,0 +1,24 @@
|
||||
<script>
|
||||
let values = $state([1, 2]);
|
||||
|
||||
async function get_total() {
|
||||
let total = 0;
|
||||
|
||||
for await (const n of values) {
|
||||
total += n;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
</script>
|
||||
|
||||
<button onclick={() => values[0]++}>a</button>
|
||||
<button onclick={() => values[1]++}>b</button>
|
||||
|
||||
<svelte:boundary>
|
||||
<h1>{await get_total()}</h1>
|
||||
|
||||
{#snippet pending()}
|
||||
<p>pending</p>
|
||||
{/snippet}
|
||||
</svelte:boundary>
|
Loading…
Reference in new issue