diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index c18ef0c25b..90e1ceb685 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -266,7 +266,7 @@ export function analyze_module(ast, options) { immutable: true, tracing: analysis.tracing, async_deriveds: new Set(), - blocking_awaits: new Set() + suspenders: new Set() }; } @@ -455,7 +455,7 @@ export function analyze_component(root, source, options) { snippets: new Set(), is_async: false, async_deriveds: new Set(), - blocking_awaits: new Set() + suspenders: new Set() }; if (!runes) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitExpression.js index 5c6d45098b..97da435d0a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitExpression.js @@ -14,7 +14,7 @@ export function AwaitExpression(node, context) { throw new Error('TODO runes mode only'); } - context.state.analysis.blocking_awaits.add(node); + context.state.analysis.suspenders.add(node); } if (context.state.expression) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 5465720a68..6755193d3c 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -219,6 +219,9 @@ export function CallExpression(node, context) { if (expression.is_async) { context.state.analysis.async_deriveds.add(node); + + context.state.analysis.is_async ||= + context.state.ast_type === 'instance' && context.state.function_depth === 1; } } else if (rune === '$inspect') { context.next({ ...context.state, function_depth: context.state.function_depth + 1 }); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitExpression.js index a26923862c..a9486fd8c8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitExpression.js @@ -7,22 +7,21 @@ import * as b from '../../../../utils/builders.js'; * @param {ComponentContext} context */ export function AwaitExpression(node, context) { - if (!context.state.analysis.runes) { + const suspend = context.state.analysis.suspenders.has(node); + + if (!suspend) { return context.next(); } - const block = context.state.analysis.blocking_awaits.has(node); - return b.call( b.member( b.await( b.call( - '$.preserve_context', - node.argument && /** @type {Expression} */ (context.visit(node.argument)), - block && b.true + '$.suspend', + node.argument && /** @type {Expression} */ (context.visit(node.argument)) ) ), - 'read' + 'exit' ) ); } diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index dcbffdfc58..fdb4eac557 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -43,7 +43,7 @@ export interface Analysis { async_deriveds: Set; /** A set of `await` expressions that should trigger suspense */ - blocking_awaits: Set; + suspenders: Set; } export interface ComponentAnalysis extends Analysis { diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 48f01aaaa9..c2d976c244 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -247,15 +247,14 @@ export function trigger_async_boundary(effect, trigger) { /** * @template T * @param {Promise} promise - * @param {boolean} block - * @returns {Promise<{ read: () => T }>} + * @returns {Promise<{ exit: () => T }>} */ -export function preserve_context(promise, block = false) { +export async function suspend(promise) { var previous_effect = active_effect; var previous_reaction = active_reaction; var previous_component_context = component_context; - let boundary = block ? active_effect : null; + let boundary = active_effect; while (boundary !== null) { if ((boundary.f & BOUNDARY_EFFECT) !== 0) { break; @@ -264,25 +263,25 @@ export function preserve_context(promise, block = false) { boundary = boundary.parent; } - if (block && boundary === null) { + if (boundary === null) { throw new Error('cannot suspend outside a boundary'); } // @ts-ignore boundary?.fn(ASYNC_INCREMENT); - return promise.then((value) => { - return { - read() { - set_active_effect(previous_effect); - set_active_reaction(previous_reaction); - set_component_context(previous_component_context); + const value = await promise; - // @ts-ignore - boundary?.fn(ASYNC_DECREMENT); + return { + exit() { + set_active_effect(previous_effect); + set_active_reaction(previous_reaction); + set_component_context(previous_component_context); - return value; - } - }; - }); + // @ts-ignore + boundary?.fn(ASYNC_DECREMENT); + + return value; + } + }; } diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index f77f39d997..0a17a54621 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -129,7 +129,7 @@ export { update_store, mark_store_binding } from './reactivity/store.js'; -export { boundary, preserve_context } from './dom/blocks/boundary.js'; +export { boundary, suspend } from './dom/blocks/boundary.js'; export { set_text } from './render.js'; export { get, diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 9fdb7abe6b..eb0fdba469 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -27,7 +27,7 @@ import { destroy_effect, render_effect } from './effects.js'; import { inspect_effects, internal_set, set_inspect_effects, source } from './sources.js'; import { get_stack } from '../dev/tracing.js'; import { tracing_mode_flag } from '../../flags/index.js'; -import { preserve_context } from '../dom/blocks/boundary.js'; +import { suspend } from '../dom/blocks/boundary.js'; /** * @template V @@ -103,7 +103,9 @@ export async function async_derived(fn) { // TODO what happens when the promise rejects? }); - (await preserve_context(promise)).read(); + // wait for the initial promise + (await suspend(promise)).exit(); + return () => get(value); }