1
0

More than 3 years have passed since last update.

cgiで時間のかかる処理をjavascriptのfetch非同期で受ける。〜プログレスバーの実装の準備〜

Posted at

目的

  • 時間のかかるサーバの処理の状態をWebブラウザでプログレスバーとして表示したいです。
  • fetchで非同期で受けられそうなので、この記事では時間のかかるcgi処理を模擬してそれをjavascriptで受けられるかの実験です。

環境

  • macOS Mojave
  • apache version
$  httpd -v
Server version: Apache/2.4.46 (Unix)
Server built:   Aug  7 2020 12:58:18
  • web browser : Firfox (macOS)

参考

参考にさせていただきました資料です。いつも先人たちの知恵に感謝です。ありがとうございます。

時間のかかるcgiの準備

  • sleepで仮想的に時間を遅らせてそれを単純にカウントアップさせます
  • バックグランド処理でも可能か試します。

cgiの準備

test_fetch_stream.cgi
#!/usr/bin/env bash
# 結果の出力
echo "Content-type: text/plain; charset=UTF-8;"
echo ""
echo "counter kicked."
for i in {1..5}; do 
    sleep 1; 
    echo $i; 
done
/var/www/cgi/test/count.bash &
exit 0
  • まずHTTPにそってContent-type:〜を記述します。
  • このcgiはメッセージとそれに続く1秒ごとの1から5のテキストの数字を返します。
  • その後、重い処理を模した子プロセスをバックグランドで起動します。

子プロセスの準備

/var/www/cgi/test/count.bash
#!/usr/bin/env bash
for i in {100..105}; do 
    sleep 20; 
    echo $i; 
done
exit 0
  • 表示はテキストで100から105まで表示します。
  • 重い処理の時間をsleepで模擬します。

htmlの準備

/var/www/html/test/test_fetch_stream.html
<!DOCTYPE html>
<html>
    <head>
     <meta charset="UTF-8" />
     <title>JavaScript test fetch</title>
    </head>
    <body>
        <h1>
            test fetch steam
        </h1>
        <button id="btn">
            fetch
        </button>
        server responce = 
         <span id="result"></span>
         <script>
            let str = new TextDecoder;
             document.addEventListener('DOMContentLoaded', function () {
                 document.getElementById('btn').addEventListener('click', function () {
                    const url = "/cgi/test/test_fetch_stream.cgi";
                     fetch(url)
                        .then(async response => {
                            console.log(response.status);
                          // response.body にレスポンス本文のストリーム(ReadableStream)が入っている
                          const reader = response.body.getReader();
                          while (true) {
                                // ストリームからデータを読む
                                const {done, value} = await reader.read();
                                const res = str.decode(new Uint8Array(value));
                                console.log(done, res);
                                document.getElementById('result').textContent =  res;
                                if (done) {
                                  // doneがtrueならストリームのデータを全部読み終わった
                                    document.getElementById('result').textContent =  "done";
                                  break;
                                }
                          }
                    })
                }, false);
             }, false);
        </script>
    </body>
</html>
  • fetch(url)でcgiを呼び出します。
  • response.body にレスポンス本文のストリーム(ReadableStream)が入っています。
  • 終了したかの判定は変数doneawaitで簡略表現してread()します。この辺りは参考文献の通りです。
  • 例外処理はtry{} catch{}async await の使い方など参照してみてください。
  • ボタンfetchを2回押すと2つのcgiが起動され、それぞれからの結果を受けるようです。cgiで複数の起動を禁止するとか処理が必要そうです。

- それでブラウザはどのくらい待てるのか(タイムアウト)が問題になりますが、30秒(sleep 30)はOKでした。sleep 60ではNGでした。子コマンドも10秒ぐらいで何か返せば小手先の対応はできそうです。

画面

スクリーンショット 2020-12-30 10.24.06.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0