diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index df96f4899b..fc64310cf5 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -63,12 +63,6 @@ export const LEGACY_PROPS = Symbol('legacy props'); export const LOADING_ATTR_SYMBOL = Symbol(''); export const PROXY_PATH_SYMBOL = Symbol('proxy path'); -/** allow users to ignore aborted signal errors if `reason.name === 'StaleReactionError` */ -export const STALE_REACTION = new (class StaleReactionError extends Error { - name = 'StaleReactionError'; - message = 'The reaction that called `getAbortSignal()` was re-run or destroyed'; -})(); - export const IS_XHTML = // We gotta write it like this because after downleveling the pure comment may end up in the wrong location !!globalThis.document?.contentType && diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js index f8f5fec290..fb61bcd8dc 100644 --- a/packages/svelte/src/internal/client/reactivity/async.js +++ b/packages/svelte/src/internal/client/reactivity/async.js @@ -1,5 +1,5 @@ /** @import { Blocker, Effect, Value } from '#client' */ -import { DESTROYED, STALE_REACTION } from '#client/constants'; +import { DESTROYED } from '#client/constants'; import { DEV } from 'esm-env'; import { component_context, @@ -270,7 +270,7 @@ export function run(thunks) { } if (aborted(active)) { - throw STALE_REACTION; + throw new StaleReactionError(); } restore(); @@ -319,3 +319,10 @@ export function increment_pending() { batch.decrement(blocking, skip); }; } + +export class StaleReactionError extends Error { + // allow users to ignore aborted signal errors if `reason.name === 'StaleReactionError` + name = 'StaleReactionError'; + message = 'The reaction that called `getAbortSignal()` was re-run or destroyed'; + batch = current_batch; +} diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index aed55f7fba..6f9f65b366 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -7,7 +7,6 @@ import { DERIVED, DIRTY, EFFECT_PRESERVED, - STALE_REACTION, ASYNC, WAS_MARKED, DESTROYED, @@ -41,7 +40,7 @@ import { async_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { component_context } from '../context.js'; import { UNINITIALIZED } from '../../../constants.js'; import { batch_values, current_batch } from './batch.js'; -import { increment_pending, unset_context } from './async.js'; +import { increment_pending, StaleReactionError, unset_context } from './async.js'; import { deferred, includes, noop } from '../../shared/utils.js'; import { set_signal_status, update_derived_status } from './status.js'; @@ -155,13 +154,13 @@ export function async_derived(fn, label, location) { } if (/** @type {Boundary} */ (parent.b).is_rendered()) { - deferreds.get(batch)?.reject(STALE_REACTION); + deferreds.get(batch)?.reject(new StaleReactionError()); deferreds.delete(batch); // delete to ensure correct order in Map iteration below } else { // While the boundary is still showing pending, a new run supersedes all older in-flight runs // for this async expression. Cancel eagerly so resolution cannot commit stale values. for (const d of deferreds.values()) { - d.reject(STALE_REACTION); + d.reject(new StaleReactionError()); } deferreds.clear(); } @@ -179,11 +178,11 @@ export function async_derived(fn, label, location) { if (decrement_pending) { // don't trigger an update if we're only here because // the promise was superseded before it could resolve - var skip = error === STALE_REACTION; + var skip = error instanceof StaleReactionError; decrement_pending(skip); } - if (error === STALE_REACTION || (effect.f & DESTROYED) !== 0) { + if (error instanceof StaleReactionError || (effect.f & DESTROYED) !== 0) { return; } @@ -205,7 +204,7 @@ export function async_derived(fn, label, location) { for (const [b, d] of deferreds) { deferreds.delete(b); if (b === batch) break; - d.reject(STALE_REACTION); + d.reject(new StaleReactionError()); } if (DEV && location !== undefined) { @@ -228,7 +227,7 @@ export function async_derived(fn, label, location) { teardown(() => { for (const d of deferreds.values()) { - d.reject(STALE_REACTION); + d.reject(new StaleReactionError()); } }); @@ -422,7 +421,7 @@ export function freeze_derived_effects(derived) { // if the effect has a teardown function or abort signal, call it if (e.teardown || e.ac) { e.teardown?.(); - e.ac?.abort(STALE_REACTION); + e.ac?.abort(new StaleReactionError()); // make it a noop so it doesn't get called again if the derived // is unfrozen. we don't set it to `null`, because the existence diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index aeffeedddd..7aed3e55a0 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -30,7 +30,6 @@ import { HEAD_EFFECT, MAYBE_DIRTY, EFFECT_PRESERVED, - STALE_REACTION, USER_EFFECT, ASYNC, CONNECTED, @@ -43,7 +42,7 @@ import { define_property } from '../../shared/utils.js'; import { get_next_sibling } from '../dom/operations.js'; import { component_context, dev_current_component_function, dev_stack } from '../context.js'; import { Batch, collected_effects } from './batch.js'; -import { flatten, increment_pending } from './async.js'; +import { flatten, increment_pending, StaleReactionError } from './async.js'; import { without_reactive_context } from '../dom/elements/bindings/shared.js'; import { set_signal_status } from './status.js'; @@ -471,7 +470,7 @@ export function destroy_effect_children(signal, remove_dom = false) { if (controller !== null) { without_reactive_context(() => { - controller.abort(STALE_REACTION); + controller.abort(new StaleReactionError()); }); } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 3260c24b8c..fd12315ea7 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -19,7 +19,6 @@ import { ROOT_EFFECT, CONNECTED, REACTION_IS_UPDATING, - STALE_REACTION, ERROR_VALUE, WAS_MARKED, MANAGED_EFFECT, @@ -58,6 +57,7 @@ import { UNINITIALIZED } from '../../constants.js'; import { captured_signals } from './legacy.js'; import { without_reactive_context } from './dom/elements/bindings/shared.js'; import { set_signal_status, update_derived_status } from './reactivity/status.js'; +import { StaleReactionError } from './reactivity/async.js'; let is_updating_effect = false; @@ -246,7 +246,7 @@ export function update_reaction(reaction) { if (reaction.ac !== null) { without_reactive_context(() => { - /** @type {AbortController} */ (reaction.ac).abort(STALE_REACTION); + /** @type {AbortController} */ (reaction.ac).abort(new StaleReactionError()); }); reaction.ac = null; diff --git a/packages/svelte/src/internal/server/abort-signal.js b/packages/svelte/src/internal/server/abort-signal.js index a769a46e3d..f3b8f1f09a 100644 --- a/packages/svelte/src/internal/server/abort-signal.js +++ b/packages/svelte/src/internal/server/abort-signal.js @@ -1,10 +1,10 @@ -import { STALE_REACTION } from '#client/constants'; +import { StaleReactionError } from '../client/reactivity/async.js'; /** @type {AbortController | null} */ let controller = null; export function abort() { - controller?.abort(STALE_REACTION); + controller?.abort(new StaleReactionError()); controller = null; }