From 7fd2d8660fb0e9cc2f325f1cca2e99c94d418935 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 17 Nov 2025 16:18:20 +0100 Subject: [PATCH] fix: parallelize async `@const`s in the template (#17165) * fix: parallelize async `@const`s in the template This fixes #17075 by solving the TODO of #17038 to add out of order rendering for async `@const` declarations in the template. It's implemented by a new field on the component state which is set as soon as we come across an async const. All async const declarations and those after it will be added to that field, and the existing blockers mechanism is then used to line up the async work correctly. After processing a fragment a `run` command is created from the collected consts. * fix * tweak --------- Co-authored-by: Rich Harris --- .changeset/social-taxis-tell.md | 5 ++ .changeset/stale-items-know.md | 5 ++ .../compiler/phases/1-parse/utils/create.js | 3 +- .../2-analyze/visitors/AwaitExpression.js | 4 -- .../phases/3-transform/client/types.d.ts | 5 ++ .../3-transform/client/visitors/ConstTag.js | 64 +++++++++++++++---- .../3-transform/client/visitors/Fragment.js | 19 ++---- .../client/visitors/SnippetBlock.js | 10 +-- .../client/visitors/SvelteBoundary.js | 14 +++- .../client/visitors/shared/fragment.js | 2 +- .../phases/3-transform/server/types.d.ts | 13 +++- .../3-transform/server/visitors/ConstTag.js | 26 +++++++- .../3-transform/server/visitors/EachBlock.js | 8 +-- .../3-transform/server/visitors/Fragment.js | 9 ++- .../3-transform/server/visitors/IfBlock.js | 6 +- .../server/visitors/SnippetBlock.js | 5 -- .../server/visitors/SvelteBoundary.js | 13 +--- .../server/visitors/shared/component.js | 7 +- .../svelte/src/compiler/types/template.d.ts | 2 - .../samples/async-const/_config.js | 5 +- .../samples/async-const/main.svelte | 10 +-- .../_expected/client/index.svelte.js | 23 ++++--- .../_expected/server/index.svelte.js | 50 +++++++++------ 23 files changed, 191 insertions(+), 117 deletions(-) create mode 100644 .changeset/social-taxis-tell.md create mode 100644 .changeset/stale-items-know.md diff --git a/.changeset/social-taxis-tell.md b/.changeset/social-taxis-tell.md new file mode 100644 index 0000000000..ea23a01def --- /dev/null +++ b/.changeset/social-taxis-tell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure async `@const` in boundary hydrates correctly diff --git a/.changeset/stale-items-know.md b/.changeset/stale-items-know.md new file mode 100644 index 0000000000..60fa85f595 --- /dev/null +++ b/.changeset/stale-items-know.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: parallelize async `@const`s in the template diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/create.js b/packages/svelte/src/compiler/phases/1-parse/utils/create.js index 2fba918f20..6030f1bd7b 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/create.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/create.js @@ -10,8 +10,7 @@ export function create_fragment(transparent = false) { nodes: [], metadata: { transparent, - dynamic: false, - has_await: false + dynamic: false } }; } 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 22a89db76e..545bc3be27 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/AwaitExpression.js @@ -26,10 +26,6 @@ export function AwaitExpression(node, context) { if (context.state.expression) { context.state.expression.has_await = true; - if (context.state.fragment && context.path.some((node) => node.type === 'ConstTag')) { - context.state.fragment.metadata.has_await = true; - } - suspend = true; } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts index 410ad120d7..d64b1d4126 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts +++ b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts @@ -51,6 +51,11 @@ export interface ComponentClientTransformState extends ClientTransformState { readonly after_update: Statement[]; /** Transformed `{@const }` declarations */ readonly consts: Statement[]; + /** Transformed async `{@const }` declarations (if any) and those coming after them */ + async_consts?: { + id: Identifier; + thunks: Expression[]; + }; /** Transformed `let:` directives */ readonly let_directives: Statement[]; /** Memoized expressions */ diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js index 81dd9e07ed..f3d7a3549c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js @@ -24,15 +24,15 @@ export function ConstTag(node, context) { expression = b.call('$.tag', expression, b.literal(declaration.id.name)); } - context.state.consts.push(b.const(declaration.id, expression)); - context.state.transform[declaration.id.name] = { read: get_value }; - // we need to eagerly evaluate the expression in order to hit any - // 'Cannot access x before initialization' errors - if (dev) { - context.state.consts.push(b.stmt(b.call('$.get', declaration.id))); - } + add_const_declaration( + context.state, + declaration.id, + expression, + node.metadata.expression.has_await, + context.state.scope.get_bindings(declaration) + ); } else { const identifiers = extract_identifiers(declaration.id); const tmp = b.id(context.state.scope.generate('computed_const')); @@ -69,13 +69,13 @@ export function ConstTag(node, context) { expression = b.call('$.tag', expression, b.literal('[@const]')); } - context.state.consts.push(b.const(tmp, expression)); - - // we need to eagerly evaluate the expression in order to hit any - // 'Cannot access x before initialization' errors - if (dev) { - context.state.consts.push(b.stmt(b.call('$.get', tmp))); - } + add_const_declaration( + context.state, + tmp, + expression, + node.metadata.expression.has_await, + context.state.scope.get_bindings(declaration) + ); for (const node of identifiers) { context.state.transform[node.name] = { @@ -84,3 +84,39 @@ export function ConstTag(node, context) { } } } + +/** + * @param {ComponentContext['state']} state + * @param {import('estree').Identifier} id + * @param {import('estree').Expression} expression + * @param {boolean} has_await + * @param {import('#compiler').Binding[]} bindings + */ +function add_const_declaration(state, id, expression, has_await, bindings) { + // we need to eagerly evaluate the expression in order to hit any + // 'Cannot access x before initialization' errors + const after = dev ? [b.stmt(b.call('$.get', id))] : []; + + if (has_await || state.async_consts) { + const run = (state.async_consts ??= { + id: b.id(state.scope.generate('promises')), + thunks: [] + }); + + state.consts.push(b.let(id)); + + const assignment = b.assignment('=', id, expression); + const body = after.length === 0 ? assignment : b.block([b.stmt(assignment), ...after]); + + run.thunks.push(b.thunk(body, has_await)); + + const blocker = b.member(run.id, b.literal(run.thunks.length - 1), true); + + for (const binding of bindings) { + binding.blocker = blocker; + } + } else { + state.consts.push(b.const(id, expression)); + state.consts.push(...after); + } +} diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index 8d6a2fac88..ff2436779b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -48,8 +48,6 @@ export function Fragment(node, context) { const is_single_child_not_needing_template = trimmed.length === 1 && (trimmed[0].type === 'SvelteFragment' || trimmed[0].type === 'TitleElement'); - const has_await = context.state.init !== null && (node.metadata.has_await || false); - const template_name = context.state.scope.root.unique('root'); // TODO infer name from parent /** @type {Statement[]} */ @@ -72,7 +70,8 @@ export function Fragment(node, context) { metadata: { namespace, bound_contenteditable: context.state.metadata.bound_contenteditable - } + }, + async_consts: undefined }; for (const node of hoisted) { @@ -153,8 +152,8 @@ export function Fragment(node, context) { body.push(...state.let_directives, ...state.consts); - if (has_await) { - body.push(b.if(b.call('$.aborted'), b.return())); + if (state.async_consts && state.async_consts.thunks.length > 0) { + body.push(b.var(state.async_consts.id, b.call('$.run', b.array(state.async_consts.thunks)))); } if (is_text_first) { @@ -177,13 +176,5 @@ export function Fragment(node, context) { body.push(close); } - if (has_await) { - return b.block([ - b.stmt( - b.call('$.async_body', b.id('$$anchor'), b.arrow([b.id('$$anchor')], b.block(body), true)) - ) - ]); - } else { - return b.block(body); - } + return b.block(body); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js index 895522d47a..1af737f05b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js @@ -14,8 +14,6 @@ export function SnippetBlock(node, context) { // TODO hoist where possible /** @type {(Identifier | AssignmentPattern)[]} */ const args = [b.id('$$anchor')]; - const has_await = node.body.metadata.has_await || false; - /** @type {BlockStatement} */ let body; @@ -78,12 +76,8 @@ export function SnippetBlock(node, context) { // in dev we use a FunctionExpression (not arrow function) so we can use `arguments` let snippet = dev - ? b.call( - '$.wrap_snippet', - b.id(context.state.analysis.name), - b.function(null, args, body, has_await) - ) - : b.arrow(args, body, has_await); + ? b.call('$.wrap_snippet', b.id(context.state.analysis.name), b.function(null, args, body)) + : b.arrow(args, body); const declaration = b.const(node.expression, snippet); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js index 49c89bc438..d64fcda2e8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js @@ -48,7 +48,11 @@ export function SvelteBoundary(node, context) { if (child.type === 'ConstTag') { has_const = true; if (!context.state.options.experimental.async) { - context.visit(child, { ...context.state, consts: const_tags }); + context.visit(child, { + ...context.state, + consts: const_tags, + scope: context.state.scopes.get(node.fragment) ?? context.state.scope + }); } } } @@ -101,7 +105,13 @@ export function SvelteBoundary(node, context) { nodes.push(child); } - const block = /** @type {BlockStatement} */ (context.visit({ ...node.fragment, nodes })); + const block = /** @type {BlockStatement} */ ( + context.visit( + { ...node.fragment, nodes }, + // Since we're creating a new fragment the reference in scopes can't match, so we gotta attach the right scope manually + { ...context.state, scope: context.state.scopes.get(node.fragment) ?? context.state.scope } + ) + ); if (!context.state.options.experimental.async) { block.body.unshift(...const_tags); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index c7f843af48..67982b6150 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -105,7 +105,7 @@ export function process_children(nodes, initial, is_element, context) { is_element && // In case it's wrapped in async the async logic will want to skip sibling nodes up until the end, hence we cannot make this controlled // TODO switch this around and instead optimize for elements with a single block child and not require extra comments (neither for async nor normally) - !(node.body.metadata.has_await || node.metadata.expression.is_async()) + !node.metadata.expression.is_async() ) { node.metadata.is_controlled = true; } else { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/types.d.ts b/packages/svelte/src/compiler/phases/3-transform/server/types.d.ts index adde7480cb..e7a72fb8ad 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/types.d.ts +++ b/packages/svelte/src/compiler/phases/3-transform/server/types.d.ts @@ -1,4 +1,10 @@ -import type { Expression, Statement, ModuleDeclaration, LabeledStatement } from 'estree'; +import type { + Expression, + Statement, + ModuleDeclaration, + LabeledStatement, + Identifier +} from 'estree'; import type { AST, Namespace, ValidatedCompileOptions } from '#compiler'; import type { TransformState } from '../types.js'; import type { ComponentAnalysis } from '../../types.js'; @@ -21,6 +27,11 @@ export interface ComponentServerTransformState extends ServerTransformState { readonly namespace: Namespace; readonly preserve_whitespace: boolean; readonly skip_hydration_boundaries: boolean; + /** Transformed async `{@const }` declarations (if any) and those coming after them */ + async_consts?: { + id: Identifier; + thunks: Expression[]; + }; } export type Context = import('zimmerframe').Context; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js index a8e4e575cc..c549d1d009 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js @@ -2,6 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import * as b from '#compiler/builders'; +import { extract_identifiers } from '../../../../utils/ast.js'; /** * @param {AST.ConstTag} node @@ -11,6 +12,29 @@ export function ConstTag(node, context) { const declaration = node.declaration.declarations[0]; const id = /** @type {Pattern} */ (context.visit(declaration.id)); const init = /** @type {Expression} */ (context.visit(declaration.init)); + const has_await = node.metadata.expression.has_await; - context.state.init.push(b.const(id, init)); + if (has_await || context.state.async_consts) { + const run = (context.state.async_consts ??= { + id: b.id(context.state.scope.generate('promises')), + thunks: [] + }); + + const identifiers = extract_identifiers(declaration.id); + const bindings = context.state.scope.get_bindings(declaration); + + for (const identifier of identifiers) { + context.state.init.push(b.let(identifier.name)); + } + + const assignment = b.assignment('=', id, init); + run.thunks.push(b.thunk(b.block([b.stmt(assignment)]), has_await)); + + const blocker = b.member(run.id, b.literal(run.thunks.length - 1), true); + for (const binding of bindings) { + binding.blocker = blocker; + } + } else { + context.state.init.push(b.const(id, init)); + } } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js index f53a3903c2..3c0a8c1676 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js @@ -34,11 +34,7 @@ export function EachBlock(node, context) { const new_body = /** @type {BlockStatement} */ (context.visit(node.body)).body; - if (node.body) - each.push( - // TODO get rid of fragment.has_await - ...(node.body.metadata.has_await ? [create_async_block(b.block(new_body))] : new_body) - ); + if (node.body) each.push(...new_body); const for_loop = b.for( b.declaration('let', [ @@ -61,7 +57,7 @@ export function EachBlock(node, context) { b.if( b.binary('!==', b.member(array_id, 'length'), b.literal(0)), b.block([open, for_loop]), - node.fallback.metadata.has_await ? create_async_block(fallback) : fallback + fallback ) ); } else { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/Fragment.js index a1d25980c4..ef5bd985ae 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/Fragment.js @@ -28,7 +28,8 @@ export function Fragment(node, context) { init: [], template: [], namespace, - skip_hydration_boundaries: is_standalone + skip_hydration_boundaries: is_standalone, + async_consts: undefined }; for (const node of hoisted) { @@ -42,5 +43,11 @@ export function Fragment(node, context) { process_children(trimmed, { ...context, state }); + if (state.async_consts && state.async_consts.thunks.length > 0) { + state.init.push( + b.var(state.async_consts.id, b.call('$$renderer.run', b.array(state.async_consts.thunks))) + ); + } + return b.block([...state.init, ...build_template(state.template)]); } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js index ca614a93e2..e8418343be 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js @@ -25,11 +25,7 @@ export function IfBlock(node, context) { const is_async = node.metadata.expression.is_async(); - const has_await = - node.metadata.expression.has_await || - // TODO get rid of this stuff - node.consequent.metadata.has_await || - node.alternate?.metadata.has_await; + const has_await = node.metadata.expression.has_await; if (is_async || has_await) { statement = create_async_block( diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js index 5fc865ec58..7ae2a8e037 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js @@ -3,7 +3,6 @@ /** @import { ComponentContext } from '../types.js' */ import { dev } from '../../../../state.js'; import * as b from '#compiler/builders'; -import { create_async_block } from './shared/utils.js'; /** * @param {AST.SnippetBlock} node @@ -16,10 +15,6 @@ export function SnippetBlock(node, context) { /** @type {BlockStatement} */ (context.visit(node.body)) ); - if (node.body.metadata.has_await) { - fn.body = b.block([create_async_block(fn.body)]); - } - // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone fn.___snippet = true; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js index 45f1b5aad2..8a30e765c2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js @@ -7,8 +7,7 @@ import { block_open, block_open_else, build_attribute_value, - build_template, - create_async_block + build_template } from './shared/utils.js'; /** @@ -43,14 +42,11 @@ export function SvelteBoundary(node, context) { ); const pending = b.call(callee, b.id('$$renderer')); const block = /** @type {BlockStatement} */ (context.visit(node.fragment)); - const statement = node.fragment.metadata.has_await - ? create_async_block(b.block([block])) - : block; context.state.template.push( b.if( callee, b.block(build_template([block_open_else, b.stmt(pending), block_close])), - b.block(build_template([block_open, statement, block_close])) + b.block(build_template([block_open, block, block_close])) ) ); } else { @@ -70,9 +66,6 @@ export function SvelteBoundary(node, context) { } } else { const block = /** @type {BlockStatement} */ (context.visit(node.fragment)); - const statement = node.fragment.metadata.has_await - ? create_async_block(b.block([block])) - : block; - context.state.template.push(block_open, statement, block_close); + context.state.template.push(block_open, block, block_close); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js index 2e7b4a186c..6f2ff38bc1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js @@ -244,12 +244,7 @@ export function build_inline_component(node, expression, context) { params.push(pattern); } - const slot_fn = b.arrow( - params, - node.fragment.metadata.has_await - ? b.block([create_async_block(b.block(block.body))]) - : b.block(block.body) - ); + const slot_fn = b.arrow(params, b.block(block.body)); if (slot_name === 'default' && !has_children_prop) { if ( diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 3960c95c8f..fd664f107c 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -48,8 +48,6 @@ export namespace AST { * Whether or not we need to traverse into the fragment during mount/hydrate */ dynamic: boolean; - /** @deprecated we should get rid of this in favour of the `$$renderer.run` mechanism */ - has_await: boolean; }; } diff --git a/packages/svelte/tests/runtime-runes/samples/async-const/_config.js b/packages/svelte/tests/runtime-runes/samples/async-const/_config.js index 8aeca875f3..c3e74e886a 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-const/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/async-const/_config.js @@ -2,11 +2,12 @@ import { tick } from 'svelte'; import { test } from '../../test'; export default test({ - html: `

Loading...

`, + mode: ['async-server', 'client', 'hydrate'], + ssrHtml: `

Hello, world!

5 01234 5 sync 6 5 0`, async test({ assert, target }) { await tick(); - assert.htmlEqual(target.innerHTML, `

Hello, world!

5 01234`); + assert.htmlEqual(target.innerHTML, `

Hello, world!

5 01234 5 sync 6 5 0`); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/async-const/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-const/main.svelte index 7410ff6a6f..b7e00803c5 100644 --- a/packages/svelte/tests/runtime-runes/samples/async-const/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/async-const/main.svelte @@ -3,17 +3,16 @@ + {@const sync = 'sync'} {@const number = await Promise.resolve(5)} - - {#snippet pending()} -

Loading...

- {/snippet} + {@const after_async = number + 1} + {@const { length, 0: first } = await '01234'} {#snippet greet()} {@const greeting = await `Hello, ${name}!`}

{greeting}

{number} - {#if number > 4} + {#if number > 4 && after_async && greeting} {@const length = await number} {#each { length }, index} {@const i = await index} @@ -23,4 +22,5 @@ {/snippet} {@render greet()} + {number} {sync} {after_async} {length} {first}
diff --git a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/client/index.svelte.js index e4df43c6c2..7d1fe4ec67 100644 --- a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/client/index.svelte.js @@ -25,20 +25,23 @@ export default function Async_in_derived($$anchor, $$props) { { var consequent = ($$anchor) => { - $.async_body($$anchor, async ($$anchor) => { - const yes1 = (await $.save($.async_derived(async () => (await $.save(1))())))(); - const yes2 = (await $.save($.async_derived(async () => foo((await $.save(1))()))))(); + let yes1; + let yes2; + let no1; + let no2; - const no1 = $.derived(() => (async () => { - return await 1; - })()); + var promises = $.run([ + async () => yes1 = (await $.save($.async_derived(async () => (await $.save(1))())))(), + async () => yes2 = (await $.save($.async_derived(async () => foo((await $.save(1))()))))(), - const no2 = $.derived(() => (async () => { + () => no1 = $.derived(() => (async () => { return await 1; - })()); + })()), - if ($.aborted()) return; - }); + () => no2 = $.derived(() => (async () => { + return await 1; + })()) + ]); }; $.if(node, ($$render) => { diff --git a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js index bece6402c6..1fd184fa79 100644 --- a/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/async-in-derived/_expected/server/index.svelte.js @@ -18,24 +18,38 @@ export default function Async_in_derived($$renderer, $$props) { } ]); - $$renderer.async_block([], async ($$renderer) => { - if (true) { - $$renderer.push(''); - - const yes1 = (await $.save(1))(); - const yes2 = foo((await $.save(1))()); - - const no1 = (async () => { - return await 1; - })(); - - const no2 = (async () => { - return await 1; - })(); - } else { - $$renderer.push(''); - } - }); + if (true) { + $$renderer.push(''); + + let yes1; + let yes2; + let no1; + let no2; + + var promises = $$renderer.run([ + async () => { + yes1 = (await $.save(1))(); + }, + + async () => { + yes2 = foo((await $.save(1))()); + }, + + () => { + no1 = (async () => { + return await 1; + })(); + }, + + () => { + no2 = (async () => { + return await 1; + })(); + } + ]); + } else { + $$renderer.push(''); + } $$renderer.push(``); });