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());
Индикация состояний загрузки
Заголовок раздела «Индикация состояний загрузки»В дополнение к фрагменту 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
будут передаваться к ближайшей границе ошибок.
Ограничения
Заголовок раздела «Ограничения»Как экспериментальная функция, детали обработки await
(и связанные с ней API, такие как $effect.pending()
) могут подвергаться изменениям, нарушающим совместимость, вне основного выпуска по семантическому версионированию, хотя мы стремимся свести такие изменения к минимуму.
В настоящее время серверный рендеринг является синхронным. Если во время SSR встречается <svelte:boundary>
с фрагментом pending
, будет отрендерен только фрагмент pending
.
Изменения, нарушающие совместимость
Заголовок раздела «Изменения, нарушающие совместимость»Эффекты выполняются в немного другом порядке, когда опция experimental.async
равна true
. В частности, эффекты блоков, такие как {#if ...}
и {#each ...}
, теперь выполняются до $effect.pre
или beforeUpdate
в том же компоненте, что означает, что в очень редких ситуациях возможно обновить блок, который больше не должен существовать, но только если вы обновляете состояние внутри эффекта, чего следует избегать.