回答編集履歴
3
実装例追記
test
CHANGED
@@ -12,3 +12,71 @@
|
|
12
12
|
「WebSockets」や「SSE」であってさえ、考慮すべきことはいろいろあって悪意のあるクライアントを切るという基準を設けないと
|
13
13
|
コネクション数が爆発してしまうことや、意図しない切断に対しどう対応するのかといったロジックがクライアント側に求められます。
|
14
14
|
|
15
|
+
## 追記
|
16
|
+
|
17
|
+
最も最小限のコードでプログレスバーを実装した事例を紹介します。
|
18
|
+
|
19
|
+
```html:index.html
|
20
|
+
<button id="start">start</button>
|
21
|
+
<progress id="progress" max="100" value="0">0%</progress>
|
22
|
+
<script>
|
23
|
+
async function interval() {
|
24
|
+
let res = await fetch("/progress");
|
25
|
+
let data = await res.json();
|
26
|
+
let progress = document.getElementById("progress");
|
27
|
+
progress.value = data.progress;
|
28
|
+
progress.innerHTML = data.progress + "%";
|
29
|
+
}
|
30
|
+
let start = document.getElementById("start");
|
31
|
+
start.addEventListener("click", async function () {
|
32
|
+
let progress = document.getElementById("progress");
|
33
|
+
progress.value = 0;
|
34
|
+
progress.innerHTML = "0%";
|
35
|
+
let id = setInterval(interval, 1000);
|
36
|
+
await fetch("/longtask");
|
37
|
+
clearInterval(id);
|
38
|
+
window.alert("long task completed");
|
39
|
+
});
|
40
|
+
</script>
|
41
|
+
```
|
42
|
+
```go:main.go
|
43
|
+
package main
|
44
|
+
|
45
|
+
import (
|
46
|
+
"encoding/json"
|
47
|
+
"log"
|
48
|
+
"net/http"
|
49
|
+
"sync"
|
50
|
+
"time"
|
51
|
+
)
|
52
|
+
|
53
|
+
type Progress struct {
|
54
|
+
sync.RWMutex
|
55
|
+
Progress int `json:"progress"`
|
56
|
+
}
|
57
|
+
|
58
|
+
func main() {
|
59
|
+
progress := &Progress{}
|
60
|
+
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
61
|
+
http.ServeFile(w, r, "index.html")
|
62
|
+
})
|
63
|
+
http.HandleFunc("/longtask", func(w http.ResponseWriter, r *http.Request) {
|
64
|
+
for i := 0; i <= 10; i++ {
|
65
|
+
progress.Lock()
|
66
|
+
progress.Progress = i * 10
|
67
|
+
progress.Unlock()
|
68
|
+
time.Sleep(time.Second)
|
69
|
+
}
|
70
|
+
})
|
71
|
+
http.HandleFunc("/progress", func(w http.ResponseWriter, r *http.Request) {
|
72
|
+
progress.RLock()
|
73
|
+
json.NewEncoder(w).Encode(progress)
|
74
|
+
progress.RUnlock()
|
75
|
+
})
|
76
|
+
if err := http.ListenAndServe(":8080", nil); err != nil {
|
77
|
+
log.Fatal(err)
|
78
|
+
}
|
79
|
+
}
|
80
|
+
```
|
81
|
+
|
82
|
+
|
2
補足追記
test
CHANGED
@@ -5,3 +5,10 @@
|
|
5
5
|
- 上記の方法はポーリングと呼ばれる方法ですが、クライアントが多かったりインターバルを短くするとバックエンドへの負荷がおおきくなりがちです。
|
6
6
|
- WebSocketsやSSE(Server Sent Events)などはクライアントごとに1コネクションを占有する代わりにバックエンドからフロントエンドへメッセージを投げ込める特性を持っています。CPUに対する負荷は値の変更時のみで済むためリアルタイム性をかなり上げることが可能ですが、クライアント数が増えるとサーバーのコネクションリソースを消費してしまう問題がつきまといます。また、切断の条件や再接続ロジックなども複雑になりがちです。
|
7
7
|
|
8
|
+
> 1度のリクエストで断続的にクライアントにレスポンスを返し続けるのは可能なのでしょうか?
|
9
|
+
|
10
|
+
これの答えはブラウザAPIがサポートしている「WebSockets」や「SSE」を使うことでできるとは思います。
|
11
|
+
ほかにもチャンクレスポンスを時間間隔を空けて返すなどはあるけども、フロントエンド側の実装が複雑になってしまいます。
|
12
|
+
「WebSockets」や「SSE」であってさえ、考慮すべきことはいろいろあって悪意のあるクライアントを切るという基準を設けないと
|
13
|
+
コネクション数が爆発してしまうことや、意図しない切断に対しどう対応するのかといったロジックがクライアント側に求められます。
|
14
|
+
|
1
別の手法について補足
test
CHANGED
@@ -3,5 +3,5 @@
|
|
3
3
|
バックエンドの負荷に関しては手法ごとにトレードオフがあるとは思います。
|
4
4
|
|
5
5
|
- 上記の方法はポーリングと呼ばれる方法ですが、クライアントが多かったりインターバルを短くするとバックエンドへの負荷がおおきくなりがちです。
|
6
|
-
- WebSocketsやSSE(Server Sent Events)などはクライアントごとに1コネクションを占有する代わりにCPUに対する負荷は値の変更時のみで済むためリアルタイム性をかなり上げることが可能ですが、クライアント数が増えるとサーバーのコネクションリソースを消費してしまう問題がつきまといます。また、切断の条件や再接続ロジックなども複雑になりがちです。
|
6
|
+
- WebSocketsやSSE(Server Sent Events)などはクライアントごとに1コネクションを占有する代わりにバックエンドからフロントエンドへメッセージを投げ込める特性を持っています。CPUに対する負荷は値の変更時のみで済むためリアルタイム性をかなり上げることが可能ですが、クライアント数が増えるとサーバーのコネクションリソースを消費してしまう問題がつきまといます。また、切断の条件や再接続ロジックなども複雑になりがちです。
|
7
7
|
|