javasciptメモ帳

javascriptに関するメモです。

87 views

はじめに

JavaScript の await を使った非同期処理は、処理の流れを一時停止しつつ、他の処理を並行して実行できる という特性を持っています。通常、await を使うと、メインスレッドがその処理の完了を待たずに他の処理(UI の更新など)を進めることができます。

しかし、処理の内容によっては await を使っても UI の更新がブロックされてしまう という問題が発生することがあります。

この記事では、
1. await の基本的な動作
2. await を使っても UI の更新が遅れる原因
3. その解決策

について解説します。


await の基本的な動作

通常、await を使うと JavaScript のメインスレッドはブロックされず、他の処理(例えば UI の更新など)を先に実行できる ようになります。

サンプルコード: await による処理の解放

async function example() {
    console.log("Start");
    await new Promise(resolve => setTimeout(resolve, 2000)); // 2秒待つ
    console.log("End");
}

example();
console.log("After function call");

このコードの出力は次のようになります。

Start
After function call
(2秒待つ)
End

ポイント:
- example()await に到達すると、2秒間待機するが、その間に "After function call" のログが出力される。
- await はメインスレッドをブロックしないため、他の処理が並行して進む

この特性を利用すると、例えばボタンをクリックしたら UI をすぐ更新し、その後に重い処理を実行する というような実装が可能になります。


await を使っても UI の更新が遅れる問題

以下のコードでは、スピナーを表示するはずなのに、ボタンを押した後すぐに表示されず、長時間待った後にやっと表示される という問題が発生します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>非同期処理と UI 更新</title>
    <style>
        #spinner {
            display: none;
            width: 50px;
            height: 50px;
            border: 5px solid lightgray;
            border-top: 5px solid blue;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <button onclick="saveFile()">ファイルを保存</button>
    <div id="spinner"></div>
    <script>
        async function saveFile() {
            const spinner = document.getElementById("spinner");
            spinner.style.display = "block"; // スピナーを表示
            console.log("start");
            await generateLargeFile(); // 時間のかかる処理
            console.log("end");
            //spinner.style.display = "none"; // スピナーを非表示
        }

        async function generateLargeFile() {
            await new Promise(resolve => {
                let a = 0;
                for(let i = 0; i < 10000000000; i++) {
                    a = a + i;
                }
                console.log(a);
                resolve();
            });
        }
    </script>
</body>
</html>

なぜ UI の更新がブロックされるのか?

  • await の直前で spinner.style.display = "block"; を実行しているにもかかわらず、スピナーがすぐに表示されない。
  • 原因は generateLargeFile() の処理がメインスレッドを完全にブロックしてしまっているから。
  • awaitPromise が解決されるまで待機するが、その Promise の処理が メインスレッドを占有する重い計算処理 だと UI の更新ができない。

解決策

1. requestAnimationFrame を使って UI を強制的に更新

async function saveFile() {
    const spinner = document.getElementById("spinner");
    spinner.style.display = "block"; // スピナーを表示

    await new Promise(resolve => requestAnimationFrame(resolve)); // UI を強制的に更新

    console.log("start");
    await generateLargeFile();
    console.log("end");

    spinner.style.display = "none"; // スピナーを非表示
}

2. setTimeout(0) を使って UI の更新を待つ

async function saveFile() {
    const spinner = document.getElementById("spinner");
    spinner.style.display = "block";

    await new Promise(resolve => setTimeout(resolve, 0)); // UI 更新を待つ

    console.log("start");
    await generateLargeFile();
    console.log("end");

    spinner.style.display = "none";
}

まとめ

  • await は非同期処理を待機するが、重い計算処理があると UI の更新がブロックされる。
  • 通常は await でメインスレッドが解放されるが、CPU を占有する処理があると UI の更新が遅れる。
  • 解決策として、requestAnimationFramesetTimeout(0) を使って UI の更新を先に実行する。

この問題を理解することで、JavaScript の非同期処理をより適切に扱えるようになります! 🚀

Page 14 of 16.

前のページ 次のページ



[添付ファイル]

1.plugin.zip  


お問い合わせ

プロフィール

すぺぺぺ

自己紹介

本サイトの作成者。
プログラムは趣味と勉強を兼ねて、のんびり本サイトを作っています。
フレームワークはdjango。
ChatGPTで自動プログラム作成に取り組み中。

サイト/ブログ

https://www.osumoi-stdio.com/novel/

ツイッター

@darkimpact0626