create new StaleReactionError each time

stale-batch
Rich Harris 4 months ago
parent 8b86bdd82d
commit 26c105b534

@ -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 &&

@ -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;
}

@ -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

@ -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());
});
}

@ -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;

@ -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;
}

Loading…
Cancel
Save