Promiseの基本

Promise

Promise(プロミス)は、非同期処理するオブジェクトです。非同期な操作が成功した場合にはresolved(解決)し、失敗したときはrejected(拒否)し、thenキーワードで処理を分岐します。

 const promise = new Promise((resolve, reject) => {​
   // 成功時は resolve(value) を呼び出す​
   // 失敗時は reject(error) を呼び出す​
 });​

​

 promise.then((value) => {​
   // resolve() で呼び出された処理​
 }).catch((error) => {​
   // reject() で呼び出された処理​
 });​
  • ペンディング(Pending): Promiseが作成された初期状態
  • 解決(Resolve): Promiseが成功した状態
  • 拒否(Rejected): Promiseが失敗した状態

async/await

async/awaitは、JavaScriptで非同期処理をより簡潔かつ直感的に扱うための構文です。

async

asyncキーワードは、非同期関数(asynchronous function)を定義するために利用します。

//非同期関数
async function getData() {

}

await

awaitキーワードは、async関数の処理完了を待機するために利用します。

functionの場合
async function getData() {
    //ネットワーク接続して、処理完了を待機を待機
    const response = await fetch('https://api.example.com/data');
}
無名関数の場合
const getData = async function() {
    //ネットワーク接続して、処理完了を待機を待機
    const response = await fetch('https://api.example.com/data');
}
アロー関数の場合
const getData = async () => {
    //ネットワーク接続して、処理完了を待機を待機
    const response = await fetch('https://api.example.com/data');
}

Promiseで非同期処理

Promiseで非同期処理をします。

ファイル構成

promise/
├── js
│   └── app.js
└── app.html
app.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ランダム名言アプリ</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>

<body class="bg-gray-100 min-h-screen flex flex-col items-center py-10">
    <div class="w-full max-w-xl bg-white p-6 rounded-lg shadow">
        <h1 class="text-2xl font-bold text-center mb-6">ランダム名言アプリ</h1>
        <div class="text-center mb-4">
            <button id="fetch-quote-button" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
                名言を取得
            </button>
        </div>
        <div id="loading" class="text-center text-gray-500 hidden">名言を取得中...</div>
        <div id="error" class="text-center text-red-500 hidden"></div>
        <div id="quote" class="mt-4 text-center text-lg font-semibold hidden"></div>
        <div id="author" class="text-center text-gray-500 hidden"></div>
    </div>
    <script src="js/app.js"></script>
</body>

</html>

Promise生成

Promiseオブジェクト

fetchRandomQuote() を定義し、Promiseオブジェクトを生成して返します。

js/app.js
    function fetchRandomQuote() {
        return new Promise((resolve, reject) => {

        });
    }

ローディング処理と待ち時間

ローディングを表示して、setTimeout() で強制的に待ち時間を作成します。

js/app.js
    function fetchRandomQuote() {
        return new Promise((resolve, reject) => {
            loadingDiv.classList.remove("hidden");
            errorDiv.classList.add("hidden");
            quoteDiv.classList.add("hidden");
            authorDiv.classList.add("hidden");

            setTimeout(() => {

            }, 2000);
        });
    }

データレスポンス処理

確率を使って成功時に resolve()、失敗時に reject() を実行します。

js/app.js
    function fetchRandomQuote() {
        return new Promise((resolve, reject) => {
            loadingDiv.classList.remove("hidden");
            errorDiv.classList.add("hidden");
            quoteDiv.classList.add("hidden");
            authorDiv.classList.add("hidden");

            setTimeout(() => {
                // 80%の確率で成功
                const success = Math.random() > 0.2;
                if (success) {
                    const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];
                    resolve(randomQuote);
                } else {
                    reject("名言の取得に失敗しました。");
                }
            }, 2000);
        });
    }

ボタンクリックイベント

ボタンクリックで handlerRandomQuote() を実行します。

js/app.js
    // ボタンクリックイベント
    fetchQuoteButton.addEventListener("click", handlerRandomQuote);

Promise実行

then() で処理

handlerRandomQuote()fetchRandomQuote() を実行します。 実行結果は、then()resolve(), reject() を分岐処理します。

js/app.js
    function handlerRandomQuote() {
        fetchRandomQuote()
            .then(quote => {
                displayQuote(quote);
            })
            .catch(error => {
                displayError(error);
            })
            .finally(() => {
                loadingDiv.classList.add("hidden");
            });
    }

    // ボタンクリックイベント
    fetchQuoteButton.addEventListener("click", handlerRandomQuote);

async/await で処理

async/await でコーディングした場合です。

js/app.js
    fetchQuoteButton.addEventListener("click", async () => {
        try {
            const quote = await fetchRandomQuote();
            displayQuote(quote);
        } catch (error) {
            displayError(error);
        } finally {
            loadingDiv.classList.add("hidden");
        }
    });

指定した時間後に、結果が表示されるか確認します。

ブラウザ

fetch API

fetch APIとは

fetch APIは、JavaScriptでネットワーク通信するためのAPIで、ブラウザアプリでサーバーとのデータの送受信ができます。XMLHttpRequestよりも現代的な非同期処理ができます。

fetch()

fetch() はネットワークリクエストするための非同期メソッドで、then() または async/await で非同期処理します。

then() の場合
fetch(URL).then()
async/awaitの場合
async function getData() {
    const response = await fetch(URL) 
}

Responseオブジェクト

非同期処理の結果は、Responseオブジェクトで取得できます。

json()

json()Responseオブジェクトのメソッドで、取得したJSONデータをJavaScriptのオブジェクトに変換します。

response.json(); 

text()

text()Responseオブジェクトのメソッドで、取得したデータをテキストで取得します。

response.text(); 

JSONオブジェクト

取得したJSONデータをJavaScriptのオブジェクト変換

JSON.parse(text)

非同期処理

then() または async/await で非同期処理します。

then()

fetch() は、then() ブロック内で非同期処理をメソッドチェインし、リクエストの成功・失敗の処理を記述できます。

// 非同期でリクエスト
fetch('https://api.example.com/data')
  .then(response => {
      // レスポンスを取得し、JSONデータを変換して返す
      return response.json();
  })
  .then(data => {
      // 変換されたデータを処理
      console.log(data);
  })
  .catch(error => {
      // 予期せぬエラー
      console.error('Error:', error);
  });

async/await

async のスコープ内で、await で非同期処理します。

async function getData() {
  try {
      // 非同期でリクエストし、レスポンスを取得
      const response = await fetch('https://api.example.com/data');
      // JSONデータを変換
      const data = await response.json(); 
      // 変換されたデータを処理
      console.log(data);
  } catch (error) {
      // 予期せぬエラー
      console.error('Error:', error);
  }
}

非同期処理(async/await)

ファイル構成

├── data/
│        └── prefectures.json
├── js/
│        └── search_address.json
└── search_address.html

都道府県データ(JSON)

都道府県データのJSONファイル、data/prefectures.json を用意します。

data/prefectures.json
[
    { "code": 1, "name": "北海道" },
    { "code": 2, "name": "青森県" },
    { "code": 3, "name": "岩手県" },
    { "code": 4, "name": "宮城県" },
    { "code": 5, "name": "秋田県" },
    { "code": 6, "name": "山形県" },
    { "code": 7, "name": "福島県" },
    { "code": 8, "name": "茨城県" },
    { "code": 9, "name": "栃木県" },
    { "code": 10, "name": "群馬県" },
    { "code": 11, "name": "埼玉県" },
    { "code": 12, "name": "千葉県" },
    { "code": 13, "name": "東京都" },
    { "code": 14, "name": "神奈川県" },
    { "code": 15, "name": "新潟県" },
    { "code": 16, "name": "富山県" },
    { "code": 17, "name": "石川県" },
    { "code": 18, "name": "福井県" },
    { "code": 19, "name": "山梨県" },
    { "code": 20, "name": "長野県" },
    { "code": 21, "name": "岐阜県" },
    { "code": 22, "name": "静岡県" },
    { "code": 23, "name": "愛知県" },
    { "code": 24, "name": "三重県" },
    { "code": 25, "name": "滋賀県" },
    { "code": 26, "name": "京都府" },
    { "code": 27, "name": "大阪府" },
    { "code": 28, "name": "兵庫県" },
    { "code": 29, "name": "奈良県" },
    { "code": 30, "name": "和歌山県" },
    { "code": 31, "name": "鳥取県" },
    { "code": 32, "name": "島根県" },
    { "code": 33, "name": "岡山県" },
    { "code": 34, "name": "広島県" },
    { "code": 35, "name": "山口県" },
    { "code": 36, "name": "徳島県" },
    { "code": 37, "name": "香川県" },
    { "code": 38, "name": "愛媛県" },
    { "code": 39, "name": "高知県" },
    { "code": 40, "name": "福岡県" },
    { "code": 41, "name": "佐賀県" },
    { "code": 42, "name": "長崎県" },
    { "code": 43, "name": "熊本県" },
    { "code": 44, "name": "大分県" },
    { "code": 45, "name": "宮崎県" },
    { "code": 46, "name": "鹿児島県" },
    { "code": 47, "name": "沖縄県" }
]

HTML作成

都道府県のプルダウンのHTML作成します。

search_address.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Search address</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>

<body>
    <main class="container">
        <h2 class="mt-3">住所検索</h2>
        <div class="mb-3">
            <label class="form-label" for="">都道府県</label>
            <select id="prefecture" class="form-control w-25" name="prefecture">
                <option value="">--- 都道府県 ---</option>
            </select>
        </div>
    </main>

    <script src="js/search_address.js"></script>
</body>

</html>

API URL生成

都道府県のJSONデータ取得APIのURLを生成します。

search_address.js
function getApiURL() {
    const currentURL = location.href
    const fileName = currentURL.substring(currentURL.lastIndexOf('/') + 1);
    const baseURL = currentURL.replace(fileName, '');
    return baseURL + 'data/prefectures.json';
}

const API_URL = getApiURL();

非同期関数作成

fetch() でAPIにアクセスし、await でデータ取得します。

search_address.js
const loadPrefectures = async () => {
    const response = await fetch(API_URL);
}

レスポンス処理

JSONデータをオブジェクトに変換します。 このときawaitで待機が必要です。

search_address.js
const loadPrefectures = async () => {
    const response = await fetch(API_URL);
    const prefectures = await response.json();
    console.log(prefectures);
}

メインプログラム

ブラウザ読み込み後に、loadPrefectures() を実行します。

search_address.js
(() => {
    loadPrefectures();
})();

コンソールで都道府県データを確認してみましょう。

結果
Array(47)

プルダウン作成

createPrefectures() で、都道府県プルダウン作成処理をします。

search_address.js
const createPrefectures = (prefectures) => {
    prefectures.forEach((prefecture) => {
        var option = document.createElement('option')
        option.value = prefecture.code;
        option.innerHTML = prefecture.name;
        document.getElementById('prefecture').appendChild(option)
    })
}

createPrefectures() を実行します。

search_address.js
const loadPrefectures = async () => {
    const response = await fetch(API_URL);
    const prefectures = await response.json();
    createPrefectures(prefectures);
}

非同期処理(then)

fetchAPIthen() で処理するケースです。

レスポンス

then() でレスポンスを取得し、json()Promiseオブジェクトを返します。

search_address.js
const loadPrefecturesForThen = () => {
    fetch(API_URL)
        .then((response) => {
            if (!response.ok) {
                throw new Error('Network error');
            }
            console.log(response.json());
            return response.json();
        })
}

データ確認

PromiseオブジェクトのPromiseResultに、データが格納されています。

結果

プルダウン作成

then() を追加し、取得しデータからプルダウンを作成します。また、catch() で予期しないエラーの対処もしておきます。

search_address.js
const loadPrefecturesForThen = () => {
    fetch(API_URL)
        .then((response) => {
            if (!response.ok) {
                throw new Error('Network error');
            }
            return response.json();
        })
        .then((prefectures) => {
            createPrefectures(prefectures);
        })
        .catch(error => {
            console.log(error);
        });
}

メインプログラム

HTML読み込み後に、loadPrefecturesForThen() を実行します。

search_address.js
(() => {
    //loadPrefectures();
    loadPrefecturesForThen();
})();