diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index 2b2967bad5..9bfa668f41 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -40,7 +40,7 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { /** @type {any} */ var component_function = DEV ? component_context?.function : null; - /** @type {V | Promise} */ + /** @type {V | Promise | null} */ var input; /** @type {Effect | null} */ @@ -146,9 +146,8 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { update(THEN, false); } - // Inert effects are proactively detached from the effect tree. Returning a noop - // teardown function is an easy way to ensure that this is not discarded - return noop; + // Set the input to null, in order to disable the promise callbacks + return () => (input = null); }); if (hydrating) { diff --git a/packages/svelte/tests/runtime-runes/samples/await-pending-destroy/_config.js b/packages/svelte/tests/runtime-runes/samples/await-pending-destroy/_config.js new file mode 100644 index 0000000000..1725cd8f6f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/await-pending-destroy/_config.js @@ -0,0 +1,94 @@ +import { test } from '../../test'; + +/** + * Polyfill for Promise.withResolver() + * @returns { {promise: Promise, resolve: (value: any)=>void, reject: (reason?: any) => void} } + */ +function promiseWithResolver() { + let resolve, reject; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + // @ts-ignore + return { promise, resolve, reject }; +} + +export default test({ + async test({ component, assert, logs }) { + { + // Set a promise on the component + const { promise, resolve } = promiseWithResolver(); + component.promise = promise; + // wait for rendering + await Promise.resolve(); + // resolve promise + resolve('ok'); + // And wait the end of the promise + await promise; + // {#await} and {:then} block must be rendered + assert.deepEqual(logs, ['await', 'then:ok']); + } + + // clear logs + logs.length = 0; + + { + // Set a promise on the component + const { promise, reject } = promiseWithResolver(); + component.promise = promise; + // wait for rendering + await Promise.resolve(); + // reject promise + reject('error'); + // And wait the end of the promise + await promise.catch((ignore) => {}); + // {#await} and {:catch} block must be rendered + assert.deepEqual(logs, ['await', 'catch:error']); + } + + // clear logs + logs.length = 0; + + { + // Set a promise on the component + const { promise, resolve } = promiseWithResolver(); + component.promise = promise; + // wait for rendering + await Promise.resolve(); + + // remove the promise + component.promise = null; + await Promise.resolve(); + + // resolve promise + resolve('ok'); + // And wait the end of the promise + await promise; + // Only {#await} block must be rendered + assert.deepEqual(logs, ['await']); + } + + // clear logs + logs.length = 0; + + { + // Set a promise on the component + const { promise, reject } = promiseWithResolver(); + component.promise = promise; + // wait for rendering + await Promise.resolve(); + + // remove the promise + component.promise = null; + await Promise.resolve(); + + // reject promise + reject('error'); + // And wait the end of the promise + await promise.catch((ignore) => {}); + // Only {#await} block must be rendered + assert.deepEqual(logs, ['await']); + } + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/await-pending-destroy/main.svelte b/packages/svelte/tests/runtime-runes/samples/await-pending-destroy/main.svelte new file mode 100644 index 0000000000..3efa926015 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/await-pending-destroy/main.svelte @@ -0,0 +1,16 @@ + + + + {#if promise} + {#await promise} + {console.log("await")} + {:then r} + {console.log("then:"+r)} + {:catch err} + {console.log("catch:"+err)} + {/await} + {/if} + +