mirror of https://github.com/sveltejs/svelte
When a stale promise is rejected in `async_derived`, and the promise eventually resolves, `d.resolve` will be noop and `d.promise.then(handler, ...)` will never run. That in turns means any restored context (via `(await save(..))()`) will never be unset. We have to handle this case and unset the context to prevent errors such as false-positive state mutation errorspull/16936/head
parent
93012e1e6f
commit
489ccc0a6d
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: unset context on stale promises
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { test } from '../../test';
|
||||||
|
|
||||||
|
export default test({
|
||||||
|
async test({ assert, target }) {
|
||||||
|
// We gotta wait a bit more in this test because of the macrotasks in App.svelte
|
||||||
|
function macrotask(t = 3) {
|
||||||
|
return new Promise((r) => setTimeout(r, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
await macrotask();
|
||||||
|
assert.htmlEqual(target.innerHTML, '<input> 1 | ');
|
||||||
|
|
||||||
|
const [input] = target.querySelectorAll('input');
|
||||||
|
|
||||||
|
input.value = '1';
|
||||||
|
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
await macrotask();
|
||||||
|
assert.htmlEqual(target.innerHTML, '<input> 1 | ');
|
||||||
|
|
||||||
|
input.value = '12';
|
||||||
|
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
await macrotask(6);
|
||||||
|
// TODO this is wrong (separate bug), this should be 3 | 12
|
||||||
|
assert.htmlEqual(target.innerHTML, '<input> 5 | 12');
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
<script>
|
||||||
|
let count = $state(0);
|
||||||
|
let value = $state('');
|
||||||
|
let prev;
|
||||||
|
|
||||||
|
function asd(v) {
|
||||||
|
const r = Promise.withResolvers();
|
||||||
|
|
||||||
|
if (prev || v === '') {
|
||||||
|
console.log('hello', !!prev)
|
||||||
|
Promise.resolve().then(async () => {
|
||||||
|
console.log('count++')
|
||||||
|
count++;
|
||||||
|
r.resolve(v);
|
||||||
|
await new Promise(r => setTimeout(r, 0));
|
||||||
|
// TODO with a microtask like below it still throws a mutation error
|
||||||
|
// await Promise.resolve();
|
||||||
|
prev?.resolve();
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log('other')
|
||||||
|
prev = Promise.withResolvers();
|
||||||
|
prev.promise.then(() => {
|
||||||
|
console.log('other coun++')
|
||||||
|
count++;
|
||||||
|
r.resolve(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = $derived(await asd(value))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input bind:value />
|
||||||
|
|
||||||
|
{count} | {x}
|
||||||
Loading…
Reference in new issue