From 37333df9080314c299cfbf6e2aa5c177c007e9a5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 5 Jun 2025 14:45:53 -0400 Subject: [PATCH] WIP --- .../svelte/src/internal/client/constants.js | 2 ++ .../src/internal/client/dom/blocks/if.js | 10 +++++++-- .../src/internal/client/reactivity/batch.js | 21 +++++++++++++++++++ .../internal/client/reactivity/deriveds.js | 4 +++- .../src/internal/client/reactivity/effects.js | 8 ++++++- .../svelte/src/internal/client/runtime.js | 13 +++++++++++- 6 files changed, 53 insertions(+), 5 deletions(-) diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index 79b98e3577..44a8839d98 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -27,6 +27,8 @@ export const EFFECT_PRESERVED = 1 << 23; // effects with this flag should not be export const REACTION_IS_UPDATING = 1 << 24; export const EFFECT_ASYNC = 1 << 25; +export const ASYNC_ERROR = 1; + export const STATE_SYMBOL = Symbol('$state'); export const LEGACY_PROPS = Symbol('legacy props'); export const LOADING_ATTR_SYMBOL = Symbol(''); diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js index a4a5b68b57..b4d98f97d8 100644 --- a/packages/svelte/src/internal/client/dom/blocks/if.js +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -122,13 +122,19 @@ export function if_block(node, fn, elseif = false) { offscreen_fragment.append((target = create_text())); } + var batch = /** @type {Batch} */ (current_batch); + + // TODO need to do this for other block types + if (pending_effect) { + // batch.skipped_effects.add(pending_effect); + // pending_effect = null; + } + if (condition ? !consequent_effect : !alternate_effect) { pending_effect = fn && branch(() => fn(target)); } if (defer) { - var batch = /** @type {Batch} */ (current_batch); - const skipped = condition ? alternate_effect : consequent_effect; if (skipped !== null) { // TODO need to do this for other kinds of blocks diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index e7fa61f483..b136dede07 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -26,7 +26,12 @@ function update_pending() { /** @type {Map | null} */ export let batch_deriveds = null; +/** TODO handy for debugging, but we should probably eventually delete it */ +let uid = 1; + export class Batch { + id = uid++; + /** @type {Map} */ #previous = new Map(); @@ -259,6 +264,22 @@ export class Batch { this.#callbacks.add(fn); } + /** @param {Effect} effect */ + skips(effect) { + /** @type {Effect | null} */ + var e = effect; + + while (e !== null) { + if (this.skipped_effects.has(e)) { + return true; + } + + e = e.parent; + } + + return false; + } + static ensure() { if (current_batch === null) { if (batches.size === 0) { diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 543b711a79..3f21da8e54 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -2,6 +2,7 @@ /** @import { Batch } from './batch.js'; */ import { DEV } from 'esm-env'; import { + ASYNC_ERROR, CLEAN, DERIVED, DESTROYED, @@ -158,7 +159,8 @@ export function async_derived(fn, location) { if (error) { if (error !== STALE_REACTION) { - handle_error(error, parent, null, parent.ctx); + signal.f |= ASYNC_ERROR; + internal_set(signal, error); } } else { internal_set(signal, value); diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 07b648c443..5a5b4d69f5 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -41,7 +41,7 @@ import { get_next_sibling } from '../dom/operations.js'; import { async_derived, derived } from './deriveds.js'; import { capture } from '../dom/blocks/boundary.js'; import { component_context, dev_current_component_function } from '../context.js'; -import { Batch } from './batch.js'; +import { Batch, current_batch } from './batch.js'; /** * @param {'$effect' | '$effect.pre' | '$inspect'} rune @@ -339,6 +339,7 @@ export function render_effect(fn, flags = 0) { * @param {Array<() => Promise>} async */ export function template_effect(fn, sync = [], async = [], d = derived) { + var batch = /** @type {Batch} */ (current_batch); var parent = /** @type {Effect} */ (active_effect); if (async.length > 0) { @@ -347,8 +348,13 @@ export function template_effect(fn, sync = [], async = [], d = derived) { Promise.all(async.map((expression) => async_derived(expression))).then((result) => { if ((parent.f & DESTROYED) !== 0) return; + // TODO probably need to do this in async.js as well + batch.restore(); + restore(); create_template_effect(fn, [...sync.map(d), ...result]); + + batch.flush(); }); } else { create_template_effect(fn, sync.map(d)); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 00051cbc23..db5ded8d63 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -33,7 +33,8 @@ import { EFFECT_IS_UPDATING, EFFECT_ASYNC, RENDER_EFFECT, - STALE_REACTION + STALE_REACTION, + ASYNC_ERROR } from './constants.js'; import { flush_tasks } from './dom/task.js'; import { internal_set, old_values } from './reactivity/sources.js'; @@ -303,6 +304,12 @@ export function reset_is_throwing_error() { * @param {ComponentContext | null} component_context */ export function handle_error(error, effect, previous_effect, component_context) { + // if the error occurred inside an effect that's + // about to be destroyed, look the other way + if (current_batch?.skips(effect)) { + return; + } + if (is_throwing_error) { if (previous_effect === null) { is_throwing_error = false; @@ -1040,6 +1047,10 @@ export function get(signal) { return batch_deriveds.get(derived); } + if ((signal.f & ASYNC_ERROR) !== 0) { + throw signal.v; + } + return signal.v; }