eyecatch

新機能 Asynchronous Svelte についての投稿があったので簡単にまとめました

Posted on 2025/05/01
# Technology

はじめに

4/28 GitHub Discussion上で Asynchronous Svelte についての投稿がありました。
よりSvelteでの開発が楽しくなりそうな内容だったので、大きな変更点等の内容を簡単にまとめてみました。ただし5/1時点の内容をまとめてます。
体験用の REPL が用意されてました。

元の Discussion は以下で、より詳細を確認できます。

以下は PR です。

背景

今まで非同期的に fetch をする場合は onMount{#await ...} block で対応していますが、これらは冗長的で処理の連携も難しく、例えば複数の fetch 処理が実行されていた場合に複数の Loader を表示するなど UX/UI 部分でもよくないです。
React, Solid, Vueなどの他のフレームワークでは <Suspense> などの コンポネント単位での非同期処理の制御を提供してます。
Svelteでも同じようなAPIを提供する必要があると考えているようです。

何ができるようになるか

以下3つの場所で await が使えるようになります。

  • <script> の topレベルでの await
  • $derived の引数として
  • Template expression の中で

Script のトップレベルでの await について

以下の Syntax のように Top Level で await が使えるようになります。

<script>
	let response = await fetch('/api/product');
	let data = await response.json();
</script>

今までは await isn't allowed in non-async function というエラーが発生していました。

<script> 内ではシークエンス通りに処理がされるようです。
例えば下記のような場合は funcA -> funcB -> funcC の順番で処理されるようです。

<script>
        await funcA(); // This is executed first
        await funcB(); // This is Executed after funcA
        await funcC(); // This is Executed after funcB
</script>

$derived の中での await について

下記のように $derived の引数に直接 await を渡すことができるようになります。

<script>
	const fetchData = async () => {
		const response = await fetch('/api/products');
		const data = await response.json();
		return data;
	}

	const data = $derived(await fetchData());
</script>

今までは const data = $derived(async () => await fetchData()); のようにする必要がありました。
こちらも同様に複数定義されてる場合は上から実行されるようです。

const data = $derived(await fetchData()); // This is executed first
const data = $derived(await fetchData()); // This is executed second
const data = $derived(await fetchData()); // This is executed third

Template expression の中での await について

以下のように Template ないでも await を使用することができるようになりました。

// Component.svelte
<script>	
	const fetchData = async (value) => {
		return new Promise((resolve) => {
			setTimeout(() => {
				resolve(value);
			}, 1000);
		});
	}
</script>

<p>{await fetchData('1秒後に表示')}</p>

<svelte:boundary>pending snippet と failed snippet が提供されて await の handling がわかりやすくなりそうです。
また template 内の await に関しては parallel で処理されるようです。

<script>
	import Component from './Component.svelte';
</script>

<svelte:boundary>
	<Component />
	
	{#snippet pending()}
		<p>loading...</p>
	{/snippet}

	{#snippet failed(error)}
		<p>oops: {error.message}</p>
	{/snippet}
</svelte:boundary>

既存の以下のやつはそのまま残りそうです。

{#await fetchData()}
  <p>Loading...</p>
{:then data}
  <p>Data: {data}</p>
{:catch error}
  <p>Error: {error.message}</p>
{/await}

Breaking changes

beforeUpdate and $effect.pre callbacks no longer run before control flow blocks are updated.

Control flow blocks ( if statement block) がアップデートされる前に beforeUpdate$effect.pre の callback は処理されなくなるそうです。

Future work

  • SSR でも resolve 後の結果が render されるように目指しているそうです。現状のpriorityは低いそうです。
  • Linkをホバーした時に起きる preload の結果も be applied または discarded されるようするそうです。これを Forking と読んでいるそうです。
  • 同時に SvelteKit の内部的な改善も行うそうです。