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