質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
86.12%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

受付中

Goでメソッドの処理状況をブラウザに返すプログレスバーは導入できるのか?

mimi_129
mimi_129

総合スコア63

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

2回答

0グッド

0クリップ

1075閲覧

投稿2022/05/21 07:39

編集2022/06/11 08:11

実現したいこと

ブラウザで使用するアプリケーションを作っています。
その中で、あるGoのメソッドで記述が多く完了まで時間が掛かるメソッドがあります。
発火はReactからのpostです。
進行状況の確認をブラウザで行いたいと思っているのですが、
そもそもそのような事は可能なのでしょうか?

コンソール上でプログレスバーを入れるライブラリは見つけましたが、
httpで行う事は難しいでしょうか?

1度のリクエストで断続的にクライアントにレスポンスを返し続けるのは可能なのでしょうか?

開発環境は、
フロントがReact、
バックエンドがGoです。

色々と初歩的な質問ですいません。

<<追記>>

App.js

1import React from 'react'; 2import axios from 'axios'; 3import imgLogo from './images/logo.png'; 4import './css/style.css' 5 6class App extends React.Component { 7 8 constructor() { 9 10 super(); 11 12 this.state = { 13 14 } 15 } 16 17 interval = async() => { 18 19 console.log("interval start") 20 21 let res = await fetch("/api/progress"); 22 let data = await res.json(); 23 let progress = document.getElementById("progress"); 24 progress.value = data.progress; 25 progress.innerHTML = data.progress + "%"; 26 } 27 28 start = async() => { 29 30 console.log("start start") 31 32 let progress = document.getElementById("progress"); 33 progress.value = 0; 34 progress.innerHTML = "0%"; 35 let id = setInterval(this.interval(), 1000); 36 await fetch("/api/longtask"); 37 clearInterval(id); 38 window.alert("long task completed"); 39 } 40 41 render() { 42 return ( 43 44 <div> 45 <div className="container"> 46 <button id="start" onClick={async () => {await this.start();} }>start</button> 47 <progress id="progress" max="100" value="0">0%</progress> 48 </div> 49 </div> 50 ) 51 } 52} 53 54export default App;

handler.go

1package handler 2 3import ( 4 // "encoding/json" 5 "fmt" 6 "net/http" 7 "sync" 8 "time" 9 10 _ "github.com/jinzhu/gorm/dialects/postgres" 11 _ "github.com/lib/pq" 12 13 "github.com/labstack/echo/v4" 14 "github.com/labstack/echo/v4/middleware" 15) 16 17type Progress struct { 18 sync.RWMutex 19 Progress int `json:"progress"` 20} 21 22func Handler() { 23 24 e := echo.New() 25 e.Use(middleware.CORS()) 26 27 // ルーティング 28 e.GET("/progress", progress) 29 e.GET("/longtask", longtask) 30 31 // local サーバー 32 e.Logger.Fatal(e.Start(":8000")) 33 34 return 35} 36 37func progress(c echo.Context) (err error) { 38 39 progress := &Progress{} 40 41 fmt.Printf("progress start") 42 43 progress.RLock() 44 c.JSON(http.StatusOK, progress) 45 progress.RUnlock() 46 47 return 48} 49 50func longtask(c echo.Context) error { 51 52 progress := &Progress{} 53 54 for i := 0; i <= 10; i++ { 55 progress.Lock() 56 progress.Progress = i * 10 57 progress.Unlock() 58 time.Sleep(time.Second) 59 } 60 61 return nil 62} 63

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

mimi_129

2022/05/28 07:49

ありがとうございます。 参考にさせていただきます。

回答2

0

最もシンプルなやり方はフロントエンド側から定期的に進捗をバックエンドに問い合わせさせることです。例えば3〜5秒に1回程度バックエンドに進捗を尋ね、得られたパーセンテージに基づく表示をReactで行えば良いでしょう。

バックエンドの負荷に関しては手法ごとにトレードオフがあるとは思います。

  • 上記の方法はポーリングと呼ばれる方法ですが、クライアントが多かったりインターバルを短くするとバックエンドへの負荷がおおきくなりがちです。
  • WebSocketsやSSE(Server Sent Events)などはクライアントごとに1コネクションを占有する代わりにバックエンドからフロントエンドへメッセージを投げ込める特性を持っています。CPUに対する負荷は値の変更時のみで済むためリアルタイム性をかなり上げることが可能ですが、クライアント数が増えるとサーバーのコネクションリソースを消費してしまう問題がつきまといます。また、切断の条件や再接続ロジックなども複雑になりがちです。

1度のリクエストで断続的にクライアントにレスポンスを返し続けるのは可能なのでしょうか?

これの答えはブラウザAPIがサポートしている「WebSockets」や「SSE」を使うことでできるとは思います。
ほかにもチャンクレスポンスを時間間隔を空けて返すなどはあるけども、フロントエンド側の実装が複雑になってしまいます。
「WebSockets」や「SSE」であってさえ、考慮すべきことはいろいろあって悪意のあるクライアントを切るという基準を設けないと
コネクション数が爆発してしまうことや、意図しない切断に対しどう対応するのかといったロジックがクライアント側に求められます。

追記

最も最小限のコードでプログレスバーを実装した事例を紹介します。

html:index.html

1<button id="start">start</button> 2<progress id="progress" max="100" value="0">0%</progress> 3<script> 4 async function interval() { 5 let res = await fetch("/progress"); 6 let data = await res.json(); 7 let progress = document.getElementById("progress"); 8 progress.value = data.progress; 9 progress.innerHTML = data.progress + "%"; 10 } 11 let start = document.getElementById("start"); 12 start.addEventListener("click", async function () { 13 let progress = document.getElementById("progress"); 14 progress.value = 0; 15 progress.innerHTML = "0%"; 16 let id = setInterval(interval, 1000); 17 await fetch("/longtask"); 18 clearInterval(id); 19 window.alert("long task completed"); 20 }); 21</script>

go:main.go

1package main 2 3import ( 4 "encoding/json" 5 "log" 6 "net/http" 7 "sync" 8 "time" 9) 10 11type Progress struct { 12 sync.RWMutex 13 Progress int `json:"progress"` 14} 15 16func main() { 17 progress := &Progress{} 18 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 19 http.ServeFile(w, r, "index.html") 20 }) 21 http.HandleFunc("/longtask", func(w http.ResponseWriter, r *http.Request) { 22 for i := 0; i <= 10; i++ { 23 progress.Lock() 24 progress.Progress = i * 10 25 progress.Unlock() 26 time.Sleep(time.Second) 27 } 28 }) 29 http.HandleFunc("/progress", func(w http.ResponseWriter, r *http.Request) { 30 progress.RLock() 31 json.NewEncoder(w).Encode(progress) 32 progress.RUnlock() 33 }) 34 if err := http.ListenAndServe(":8080", nil); err != nil { 35 log.Fatal(err) 36 } 37}

投稿2022/05/22 11:55

編集2022/05/28 12:50
nobonobo

総合スコア3259

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

mimi_129

2022/05/28 08:06

詳しい回答ありがとうございます。 「WebSockets」や「SSE」初めてお聞きしました。 参考にさせていただきます。 >最もシンプルなやり方はフロントエンド側から定期的に進捗をバックエンドに問い合わせさせることです。例えば3〜5秒に1回程度バックエンドに進捗を尋ね、得られたパーセンテージに基づく表示をReactで行えば良いでしょう。 こちらを参考にプログラムを組んでみました(追記しました) しかし、狙い通りの動作になっていないようです。。 longMethodは動作し続けるのですが、 situationTimeを定期実行させても値が取得できないです。 (Uncaught SyntaxError: Unexpected identifier) axiosの並行実行がうまくいっていないと思うのですが。。
nobonobo

2022/05/28 12:51

最小のコードで実装例を追記しました。参考に。
mimi_129

2022/06/04 08:05

おお。素晴らしい。いけてますね。 すいません。 これってReact+echoでも実装できますかね。。 現在絶賛格闘中なんですが、参考になるコードをいただけたらありがたいのですが。 質問の主題からはズレているので難しそうなら大丈夫です
nobonobo

2022/06/04 08:36

どちらも使ったことがないので直接的なアドバイスは難しいと思います。react+echo版のやってみたコードを追記してもらえればアドバイスできるかもしれません。
mimi_129

2022/06/11 06:17

うーんやっぱり難しいですよね。。 >react+echo版のやってみたコードを追記してもらえればアドバイスできるかもしれません。 一応追記に現状のコードを記述しました。 何かアドバイスいただけますと幸いです。
nobonobo

2022/06/11 07:38

longtaskのハンドリングが無いのでは?
mimi_129

2022/06/11 08:22

あーすいません。。 そちらに関しては単純に記述漏れでした。 longtaskのハンドリング追加しましたが、 狙い通りの動作をしていないみたいです。 懸念点として、 ◯App.js内のintervalメソッド内で受け取っているjsonがおかしい? ◯handler.goの各々メソッド内でProgressのインスタンスを生成しているが、 これがおかしい? ◯handler.go内、progressメソッドのレスポンスがおかしい? 辺りが考えられますが、 現状修正方法を模索中です。。

0

真面目に、プログレスバーを実装しようとするなら、その時間のかかる処理というやつから、処理割合に対応した通知を飛ばす必要があります
たとえば、処理が10%済むごとに通知を飛ばす(実際にはなにかコールバックさせるとか)、と言うコードを組む必要がある、ってことですね
それが可能なら、あなたの想定しているようなプログレスバーを出す、のは可能かと思います

ましかし、よのなかで出しているプログレスバー、ってのは、たいてい、処理とは関係なく一定時間で勝手にバーが進むようにしておいて、100%で勝手に処理終了としてしまう、ってのになっています

投稿2022/05/21 09:05

y_waiwai

総合スコア86013

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

2022/05/21 10:49

こちらの回答が複数のユーザーから「過去の低評価」という指摘を受けました。

回答へのコメント

Zuishin

2022/05/21 11:15 編集

> 通知を飛ばす必要があります 必要はないでしょう。 別に通知を飛ばさなくても、フロントの処理を 10 回に分ければいいだけだと思います。 > ましかし、よのなかで出しているプログレスバー、ってのは、たいてい、処理とは関係なく一定時間で勝手にバーが進むようにしておいて、100%で勝手に処理終了としてしまう、ってのになっています 質問と関係ありませんし、処理とは関係なく一定時間で 100% になるプログレスバーはまだ見たことがありません。

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
86.12%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。