Перейти к содержимому

await

Начиная со Svelte 5.36 ключевое слово await можно использовать в компонентах в трёх новых местах:

  • на верхнем уровне <script> компонента
  • внутри выражений $derived(...)
  • непосредственно в разметке

Данная функциональность пока экспериментальна — для её использования необходимо добавить параметр experimental.async в конфигурации Svelte:

svelte.config.js
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 в том же компоненте, что означает, что в очень редких ситуациях возможно обновить блок, который больше не должен существовать, но только если вы обновляете состояние внутри эффекта, чего следует избегать.