MochiuWiki : SUSE, EC, PCB
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
JavaScriptの基礎 - async awaitのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
JavaScriptの基礎 - async await
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == async / await は ES2017 (ES8) で導入された非同期処理の構文であり、Promiseをベースとして構築されている。<br> async関数は常にPromiseを返し、await式はPromiseの解決を待機してその値を取得する。<br> <br> async / await を使用することにより、非同期処理をまるで同期処理のように記述できる。<br> Promiseチェーン (.then / .catch) と比較して、コードの読みやすさと保守性が大幅に向上する。<br> <br> エラーハンドリングには try / catch / finally構文を使用する。<br> これにより、同期処理と非同期処理のエラーを統一した方法で扱うことができる。<br> <br> async / await は逐次処理と並列処理の両方に対応している。<br> 複数の非同期処理を並列実行する場合は <code>Promise.all</code> と組み合わせることで効率的な処理が可能になる。<br> <br> ES2022以降では、ESモジュールのトップレベルでもawaitを使用できる (トップレベルawait)。<br> また、非同期イテレータとの組み合わせにより <code>for await...of</code> 構文も利用可能である。<br> <br> <u>Tauriアプリケーション開発においては、RustバックエンドとのIPC通信に <code>invoke()</code> 関数を使用するが、この関数はPromiseを返すため、async / await との親和性が高い。</u><br> <br> Tauri(Rust + React)では、フロントエンド(WebView)と バックエンド(Rust)間のプロセス間通信を <u>Tauri IPC</u> と呼ぶ。<br> <br><br> == async関数 == ==== async関数の宣言 ==== async関数は <code>async</code> キーワードを付与することで定義する。<br> 関数宣言、関数式、アロー関数、メソッド定義等、全ての関数定義形式に対応している。<br> <br> <syntaxhighlight lang="javascript"> // 関数宣言 async function fetchData() { const response = await fetch('/api/data'); return response.json(); } // 関数式 const fetchData = async function() { const response = await fetch('/api/data'); return response.json(); }; // アロー関数 const fetchData = async () => { const response = await fetch('/api/data'); return response.json(); }; // オブジェクトメソッド const obj = { async fetchData() { const response = await fetch('/api/data'); return response.json(); } }; // クラスメソッド class DataService { async fetchData() { const response = await fetch('/api/data'); return response.json(); } } </syntaxhighlight> <br> ==== async関数の戻り値 ==== async関数の戻り値は、常にPromiseでラップされる。<br> この動作はJavaScriptエンジンによって自動的に処理される。<br> <br> <syntaxhighlight lang="javascript"> async function example() { return 42; } // 上記は以下と同等 function example() { return Promise.resolve(42); } example().then(value => console.log(value)); // 42 </syntaxhighlight> <br> 戻り値のパターンは以下の通りである。<br> <br> <center> {| class="wikitable" |+ async関数の戻り値の変換 ! 記述 !! 変換結果 |- | return <値> || <code>Promise.resolve(値)</code> に変換される |- | throw <エラー> || <code>Promise.reject(エラー)</code> に変換される |- | return (値なし) || <code>Promise.resolve(undefined)</code> に変換される |} </center> <br> <syntaxhighlight lang="javascript"> async function resolved() { return 'success'; } async function rejected() { throw new Error('failure'); } async function empty() { return; } resolved().then(v => console.log(v)); // "success" rejected().catch(e => console.error(e.message)); // "failure" empty().then(v => console.log(v)); // undefined </syntaxhighlight> <br><br> == await式 == ==== awaitの基本 ==== <code>await</code> 式は async関数の内部でのみ使用可能である。(ES2022以降のトップレベルawaitを除く)<br> <code>await</code> の後にPromiseを記述することにより、そのPromiseが解決されるまで処理を一時停止し、解決値を取得する。<br> <br> <syntaxhighlight lang="javascript"> async function example() { // Promiseが解決されるまで待機する const value = await somePromise; console.log(value); // 非Promiseの値にもawaitは使用できる (即座に値を返す) const number = await 42; console.log(number); // 42 } </syntaxhighlight> <br> <code>await</code> されたPromiseが拒否された場合、awaitの箇所で例外がスローされる。<br> この例外は、try / catchで捕捉できる。<br> <br> <syntaxhighlight lang="javascript"> async function example() { try { const value = await Promise.reject(new Error('rejected')); } catch (error) { console.error(error.message); // "rejected" } } </syntaxhighlight> <br> ==== awaitとPromiseチェーンの比較 ==== async / await は Promiseチェーンと同等の処理を、より読みやすい形式で記述するための構文糖衣 (Syntactic Sugar) である。<br> 以下に同じ処理をPromiseチェーンとasync / awaitで記述した例を示す。<br> <br> * Promiseチェーン (Before) *: <syntaxhighlight lang="javascript"> function fetchUserData(userId) { return fetch(`/users/${userId}`) .then(response => response.json()) .then(user => { return fetch(`/posts/${user.id}`) .then(response => response.json()) .then(posts => ({ user, posts })); }) .catch(error => console.error(error)); } </syntaxhighlight> *: <br> * async / await (After) *: <syntaxhighlight lang="javascript"> async function fetchUserData(userId) { try { const response = await fetch(`/users/${userId}`); const user = await response.json(); const postsResponse = await fetch(`/posts/${user.id}`); const posts = await postsResponse.json(); return { user, posts }; } catch (error) { console.error(error); } } </syntaxhighlight> <br> async / await を使用することにより、ネストが解消されてソースコードのフローが上から下へ直線的に読めるようになる。<br> <code>.then</code> / <code>.catch</code> の詳細については、[[JavaScriptの基礎 - Promise]]のページを参照すること。<br> <br><br> == エラーハンドリング == ==== try / catch / finally ==== async関数内のエラーハンドリングには try / catch / finally 構文を使用する。<br> この構文は同期処理のエラーハンドリングと同じ記法であり、直感的に理解できる。<br> <br> <syntaxhighlight lang="javascript"> async function processData(url) { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } return await response.json(); } catch (error) { console.error(error.message); return null; } finally { console.log('処理完了'); // 成功・失敗に関わらず実行される } } </syntaxhighlight> <br> try / catch / finally の各ブロックの役割を以下に示す。<br> <br> <center> {| class="wikitable" |+ try / catch / finally の各ブロックの役割 ! ブロック !! 説明 |- | tryブロック | * 正常な処理フローを記述する。 * await式はこのブロック内に記述する。 |- | catchブロック | * エラーが発生した場合の処理を記述する。 * awaitされたPromiseの拒否もここで捕捉される。 |- | finallyブロック | * 成功・失敗に関わらず必ず実行される処理を記述する。 * リソースの解放やローディング状態の解除に使用する。 |} </center> <br> <u>下表に、<code>return await</code>についての注意事項を示す。</u><br> <br> <u>try / catch ブロックの内部では <code>return await</code> を使用する必要がある。</u><br> <u><code>await</code> を省略すると、Promiseが拒否された場合にcatchブロックで捕捉できなくなるためである。</u><br> <br> <syntaxhighlight lang="javascript"> // OK : try / catch内ではreturn awaitが必要 async function withErrorHandling() { try { return await riskyOperation(); // awaitが必要 } catch (error) { return null; } } // OK : try / catch無しの場合、awaitは不要 (余分なマイクロタスクを避けられる) async function simple() { return fetchData(); // awaitは不要 } </syntaxhighlight> <br> ==== 複数のawaitでのエラー処理 ==== 複数のawait式が存在する場合、try / catch を1つにまとめると最初のエラーで後続の処理が中断される。<br> 各awaitを個別のtry / catchで囲むことで、それぞれのエラーを独立して処理できる。<br> <br> <syntaxhighlight lang="javascript"> async function processMultiple() { let user, posts; // ユーザ取得のエラーを個別に処理する try { user = await fetchUser(); } catch (error) { console.error('ユーザー取得エラー:', error); user = null; } // 投稿取得のエラーを個別に処理する try { posts = await fetchPosts(); } catch (error) { console.error('投稿取得エラー:', error); posts = []; } return { user, posts }; } </syntaxhighlight> <br> この方法により、ユーザ取得に失敗しても投稿取得の処理を継続できる。<br> どちらのエラーも許容できない場合は、1つの try / catch にまとめてよい。<br> <br><br> == 逐次処理と並列処理 == ==== 逐次実行 ==== await式を順番に記述すると、前のPromiseが解決されてから次のPromiseを開始する逐次実行となる。<br> 各処理が前の処理の結果に依存する場合は、逐次実行が適切である。<br> <br> <syntaxhighlight lang="javascript"> async function sequential() { // 各リクエストが完了してから次を開始する (合計: 2 + 3 + 1 = 6秒) const user = await fetch('/user').then(r => r.json()); // 2秒 const posts = await fetch('/posts').then(r => r.json()); // 3秒 const comments = await fetch('/comments').then(r => r.json()); // 1秒 return { user, posts, comments }; } </syntaxhighlight> <br> ==== 並列実行 (Promise.allとの組み合わせ) ==== 互いに依存しない複数のPromiseは、<code>Promise.all</code> を使用して並列実行できる。<br> <code>Promise.all</code> は全てのPromiseが解決されたときに解決する新しいPromiseを返す。<br> <br> <syntaxhighlight lang="javascript"> async function parallel() { // 全てのリクエストを同時に開始する (合計: max(2, 3, 1) = 3秒) const [user, posts, comments] = await Promise.all([ fetch('/user').then(r => r.json()), fetch('/posts').then(r => r.json()), fetch('/comments').then(r => r.json()) ]); return { user, posts, comments }; } </syntaxhighlight> <br> 並列実行では、最も時間のかかる処理が全体の待機時間を決定する。<br> 上記の例では、6秒から3秒へと待機時間が半減する。<br> <br> <code>Promise.all</code> はいずれかのPromiseが拒否された時点でただちに拒否される。<br> 全ての結果が必要な場合は <code>Promise.all</code> を、一部の失敗を許容する場合は <code>Promise.allSettled</code> を使用する。<br> <br> ==== 注意 : awaitの連続は直列化される ==== <u>配列リテラル内でawaitを連続して記述すると、直列実行になってしまうため注意が必要である。</u><br> <br> <syntaxhighlight lang="javascript"> // OK : 並列実行 (全て同時に開始する) const [r1, r2, r3] = await Promise.all([promise1, promise2, promise3]); // NG : 直列実行になる (promise1 -> promise2 -> promise3 の順に待機する) const results = [await promise1, await promise2, await promise3]; </syntaxhighlight> <br> <u>Promise.allに渡す配列を生成する時は、Promiseオブジェクト (またはPromiseを返す関数呼び出し) を先に評価してから <code>await Promise.all()</code> で待機する点に注意する。</u><br> <br><br> == トップレベルawait == ==== トップレベルawaitとは ==== ES2022で導入されたトップレベルawaitは、ESモジュールのトップレベル (async関数の外側) でawaitを使用できる機能である。<br> これにより、モジュールの初期化処理に非同期処理を組み込むことができる。<br> <br> <syntaxhighlight lang="javascript"> // config.mjs (ESモジュール) const config = await fetch('/config.json').then(r => r.json()); export { config }; </syntaxhighlight> <br> <syntaxhighlight lang="javascript"> // database.mjs const db = await initializeDatabase(); export async function query(sql) { return db.execute(sql); } </syntaxhighlight> <br> ==== 使用上の注意点 ==== 下表に、トップレベルawaitを使用する時の制約と注意事項を示す。<br> <br> <center> {| class="wikitable" |+ top-level awaitの使用可否 ! 使用可否 !! 環境 |- | 使用可能 | * <u>.mjsファイル</u> * <u>package.jsonファイル</u> で <code>"type": "module"</code> を指定した.jsファイル * <code>type="module"</code> 属性を付与した <code><script></code> タグ |- | 使用不可 | * CommonJSモジュール (<code>require()</code> 形式) * <code>type="module"</code> のない通常の <code><script></code> タグ |} </center> <br> <u>トップレベルawaitを使用するモジュールをインポートする側は、そのモジュールのawaitが解決されるまでインポートが完了しない。</u><br> <u>モジュールの依存関係が深い場合、初期化時間が長くなる可能性があるため注意すること。</u><br> <br><br> == asyncイテレーション == ==== for await...of ==== <code>for await...of</code> 構文は、非同期イテラブルオブジェクトを順次処理するための構文である。<br> 非同期ジェネレータ関数 (<code>async function*</code>) と組み合わせることにより、ストリーム処理やページネーション処理を直感的に記述できる。<br> <br> <syntaxhighlight lang="javascript"> // 非同期ジェネレータ関数でページネーションを実装する async function* fetchPages(url) { let page = 1; while (true) { const response = await fetch(`${url}?page=${page}`); const data = await response.json(); if (data.items.length === 0) break; for (const item of data.items) { yield item; } page++; } } // for await...ofで非同期イテレータを消費する async function processAllPosts() { for await (const item of fetchPages('/api/posts')) { console.log(item.title); } } </syntaxhighlight> <br> <code>for await...of</code> は非同期ジェネレータだけでなく、<code>Symbol.asyncIterator</code> を実装したオブジェクトにも使用できる。<br> これは、Node.jsのReadableStream等、データの逐次読み取りが必要な場面で活用できる。<br> <br><br> == Tauriでの使用例 == Tauriアプリケーションでは、<code>invoke()</code> 関数を使用してRustバックエンドのコマンドを呼び出す。<br> <code>invoke()</code> はPromiseを返すため、async / await との組み合わせが自然である。<br> <br> Reactコンポーネントでの基本的な使用例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> import { useEffect, useState } from 'react'; import { invoke } from '@tauri-apps/api/core'; function UserList() { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false); useEffect(() => { // useEffect内でasync関数を定義して即時実行する const loadUsers = async () => { setLoading(true); try { const result = await invoke('get_users'); setUsers(result); } catch (error) { console.error(error); } finally { setLoading(false); } }; loadUsers(); }, []); const handleDelete = async (id) => { try { await invoke('delete_user', { id }); setUsers(prev => prev.filter(u => u.id !== id)); } catch (error) { console.error(error); } }; if (loading) return <div>読み込み中...</div>; return ( <ul> {users.map(user => ( <li key={user.id}> {user.name} <button onClick={() => handleDelete(user.id)}>削除</button> </li> ))} </ul> ); } </syntaxhighlight> <br> useEffect内でasync関数を直接渡すことはできないため、内部でasync関数を定義して即時実行するパターンを使用する。<br> <br> 複数のTauriコマンドを並列実行する例を以下に示す。<br> <br> <syntaxhighlight lang="javascript"> async function loadInitialData() { // 複数のTauriコマンドを並列実行する const [users, settings] = await Promise.all([ invoke('get_users'), invoke('get_settings') ]); return { users, settings }; } </syntaxhighlight> <br> 複数のTauriコマンドに依存関係がない場合は、<code>Promise.all</code> を使用して並列実行することでパフォーマンスを向上できる。<br> <br><br> == 関連情報 == * [[JavaScriptの基礎 - Promise]] * [[JavaScriptの基礎 - イベントループ]] * [[JavaScriptの基礎 - Fetch API]] * [[JavaScriptの基礎 - コールバック関数]] <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:Web]]
JavaScriptの基礎 - async await
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse