目的
- 時間のかかるサーバの処理の状態を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)
参考
参考にさせていただきました資料です。いつも先人たちの知恵に感謝です。ありがとうございます。
- まだXMLHttpRequestを使ってるの? fetchのすすめ←(ほぼこの使い方です)
- JavaScript、Node.js で文字列とバイト列の相互変換
- サーバーでの重い処理の経過をリアルタイムに通知する (←これは使っていません)
時間のかかる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)が入っています。
- 終了したかの判定は変数
done
にawait
で簡略表現してread()
します。この辺りは参考文献の通りです。 - 例外処理は
try{} catch{}
でasync await の使い方など参照してみてください。 - ボタン
fetch
を2回押すと2つのcgiが起動され、それぞれからの結果を受けるようです。cgiで複数の起動を禁止するとか処理が必要そうです。 - それでブラウザはどのくらい待てるのか(タイムアウト)が問題になりますが、30秒(
sleep 30
)はOKでした。sleep 60
ではNGでした。子コマンドも10秒ぐらいで何か返せば小手先の対応はできそうです。