await
Начиная со Svelte 5.36 ключевое слово await можно использовать в компонентах в трёх новых местах:
- на верхнем уровне
<script>компонента - внутри выражений
$derived(...) - непосредственно в разметке
Данная функциональность пока экспериментальна — для её использования необходимо добавить параметр experimental.async в конфигурации Svelte:
export default { compilerOptions: { experimental: { async: true } }};Экспериментальный флаг будет удалён в Svelte 6.
Границы
Заголовок раздела «Границы»В текущей реализации await можно использовать только внутри <svelte:boundary> при наличии сниппета pending:
<svelte:boundary> <MyApp />
{#snippet pending()} <p>загрузка...</p> {/snippet}</svelte:boundary>Это ограничение будет снято после реализации асинхронного серверного рендеринга в Svelte (см. ограничения).
Синхронизированные обновления
Заголовок раздела «Синхронизированные обновления»Когда выражение await зависит от определённого состояния, изменения этого состояния не будут отражены в интерфейсе до завершения асинхронной операции. Это предотвращает попадание интерфейса в несогласованное состояние. Например, в ситуации как здесь…
<script> let a = $state(1); let b = $state(2);
async function add(a, b) { await new Promise((f) => setTimeout(f, 500)); // искусственная задержка return a + b; }</script>
<input type="number" bind:value={a}><input type="number" bind:value={b}>
<p>{a} + {b} = {await add(a, b)}</p>…если вы увеличите a, содержимое <p> не сразу обновится, чтобы отобразить это:
<p>2 + 2 = 3</p>Вместо этого текст обновится до 2 + 2 = 4, когда add(a, b) завершится.
Обновления могут перекрываться — быстрое обновление отобразится в интерфейсе, пока предыдущее медленное обновление всё ещё выполняется.
Параллелизм
Заголовок раздела «Параллелизм»Svelte будет выполнять как можно больше асинхронной работы параллельно. Например, если у вас есть два выражения await в вашей разметке…
<p>{await one()}</p><p>{await two()}</p>…обе функции будут выполняться одновременно, поскольку они являются независимыми выражениями, несмотря на то, что они визуально последовательны.
Это не относится к последовательным выражениям await внутри вашего <script> или внутри асинхронных функций — они выполняются как любой другой асинхронный JavaScript. Исключением является то, что независимые выражения $derived будут обновляться независимо, хотя они будут выполняться последовательно при их первом создании:
// они будут выполняться последовательно при первом запуске,// но будут обновляться независимоlet a = $derived(await one());let b = $derived(await two());Индикация состояний загрузки
Заголовок раздела «Индикация состояний загрузки»Чтобы отобразить заполняющий пользовательский интерфейс, вы можете обернуть содержимое в <svelte:boundary> с фрагментом pending. Этот фрагмент будет показан при первом создании границы, но не для последующих обновлений, которые координируются глобально.
После того как содержимое границы разрешилось в первый раз и заменило фрагмент pending, вы можете отслеживать последующую асинхронную работу с помощью $effect.pending(). Это можно использовать, например, для отображения спиннера «мы асинхронно проверяем ваш ввод» рядом с полем формы.
Вы также можете использовать settled(), чтобы получить промис, который разрешается, когда текущее обновление завершено:
import { tick, settled } from 'svelte';
async function onclick() { updating = true;
// без этого изменение `updating` будет // сгруппировано с другими изменениями, что означает, // что оно не отобразится в интерфейсе await tick();
color = 'octarine'; answer = 42;
await settled();
// все обновления, зависящие от `color` или `answer`, // теперь применены updating = false;}Обработка ошибок
Заголовок раздела «Обработка ошибок»Ошибки в выражениях await будут передаваться к ближайшей границе ошибок.
Серверный рендеринг
Заголовок раздела «Серверный рендеринг»Svelte поддерживает асинхронный рендеринг на стороне сервера (SSR) с помощью API render(...). Чтобы использовать его, просто дождитесь возвращаемого значения:
import { render } from 'svelte/server';import App from './App.svelte';
const { head, body } = await render(App);Если во время SSR встречается <svelte:boundary> с фрагментом pending, этот фрагмент будет отрендерен, в то время как остальной контент будет проигнорирован. Все выражения await, встреченные вне границ с фрагментами pending, будут разрешены и отрендерены до возврата await render(...).
Форкинг
Заголовок раздела «Форкинг»API fork(...), появившийся в версии 5.42, позволяет запускать выражения await, которые, как вы ожидаете, выполнятся в ближайшем будущем. Это в первую очередь предназначено для фреймворков вроде SvelteKit, чтобы реализовывать предварительную загрузку — например, когда пользователь даёт сигнал о предстоящей навигации.
<script> import { fork } from 'svelte'; import Menu from './Menu.svelte';
let open = $state(false);
/** @type {import('svelte').Fork | null} */ let pending = null;
function preload() { pending ??= fork(() => { open = true; }); }
function discard() { pending?.discard(); pending = null; }</script>
<button onfocusin={preload} onfocusout={discard} onpointerenter={preload} onpointerleave={discard} onclick={() => { pending?.commit(); pending = null;
// на случай, если `pending` не существовало // (если оно существовало, это ничего не делает) open = true; }}>open menu</button>
{#if open} <!-- любая асинхронная работа внутри этого компонента запустится сразу после создания форка --> <Menu onclose={() => open = false} />{/if}Ограничения
Заголовок раздела «Ограничения»Как экспериментальная функция, детали обработки await (и связанные с ней API, такие как $effect.pending()) могут подвергаться изменениям, нарушающим совместимость, вне основного выпуска по семантическому версионированию, хотя мы стремимся свести такие изменения к минимуму.
В настоящее время серверный рендеринг является синхронным. Если во время SSR встречается <svelte:boundary> со сниппетом pending, будет отрендерен только сниппет pending.
Изменения, нарушающие совместимость
Заголовок раздела «Изменения, нарушающие совместимость»Эффекты выполняются в немного другом порядке, когда опция experimental.async равна true. В частности, эффекты блоков, такие как {#if ...} и {#each ...}, теперь выполняются до $effect.pre или beforeUpdate в том же компоненте, что означает, что в очень редких ситуациях возможно обновить блок, который больше не должен существовать, но только если вы обновляете состояние внутри эффекта, чего следует избегать.