mirror of https://github.com/sveltejs/svelte
fix: unset context on stale promises (#16935)
* fix: unset context on stale promises 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 errors * fix: unset context on stale promises (slightly different approach) (#16936) * slightly different approach to #16935 * move unset_context call * get rid of logs --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>pull/16943/head
parent
b05e12fd63
commit
23e2bb3b89
@ -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,34 @@
|
|||||||
|
<script>
|
||||||
|
let count = $state(0);
|
||||||
|
let value = $state('');
|
||||||
|
let prev;
|
||||||
|
|
||||||
|
function asd(v) {
|
||||||
|
const r = Promise.withResolvers();
|
||||||
|
|
||||||
|
if (prev || v === '') {
|
||||||
|
Promise.resolve().then(async () => {
|
||||||
|
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 {
|
||||||
|
prev = Promise.withResolvers();
|
||||||
|
prev.promise.then(() => {
|
||||||
|
count++;
|
||||||
|
r.resolve(v)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
const x = $derived(await asd(value))
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input bind:value />
|
||||||
|
|
||||||
|
{count} | {x}
|
||||||
Loading…
Reference in new issue