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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Redis

Redisは、オープンソースのkey-valueデータストアで、NoSQLに分類されます。すべてのデータをメモリ上に保存するため、処理が極めて高速です。

Q&A

解決済

4回答

2710閲覧

【ruby on rails】railsで非同期処理時に返り値をうけとりたい

FumiakiNakao

総合スコア180

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Redis

Redisは、オープンソースのkey-valueデータストアで、NoSQLに分類されます。すべてのデータをメモリ上に保存するため、処理が極めて高速です。

0グッド

0クリップ

投稿2018/07/26 14:56

編集2018/07/26 22:33

ruby on railsで、非同期処理の結果を受け取って、controllerが呼び出された際にブラウザ上に表示したいです。
activeJobとSidekiqを用いることを想定しています。

現状は以下の通りです。

  • job

ruby

1#app/jobs/test_job.rb 2 class TestJob < ApplicationJob 3 queue_as :default 4 5 def perform(**message) 6 message["hello"]="world" 7 return message 8 end 9 end
  • controller
#app/controllers/test_controller.rb class TestController < ApplicationController def index @message={} @message=TestJob.perform_later(@message) render json: @message end end
  • 理想とする表示
{hello:world}
  • 実際の表示
{ "arguments":[ {} ], "job_id": "4b55f945-a2a2-4711-9229-060c0a567aef", "queue_name": "default", "priority": null, "executions": 0, "provider_job_id": "e04782f39c95840de0689a70" }

どうやら自分のコードだとmessageはenqueueの成功状況を受け取っているっぽい&ActiveJobでreturnはつかえなさそう ということは想像がついたのですが、正しい方法に自力では行きつけませんでした。

どなたかご教授お願いいたします。

また先述の通り非同期処理にはsidekiqを使っていますが、sidekiqで無理ということであれば別のgemもしくはやり方を教えていただけると助かります。

(追記)
最終的には、とあるAPIから10件程度のデータをうけとって、そのデータ1件1件を別のあるAPIに投げていき、それらすべての結果を表示するといったことをやりたいと考えています。
別APIからのレスポンスを受け取るのが、1件あたり2秒、合計で20秒ほどかかってしまうので、非同期処理にしようとしてる次第です

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

なんとなく、非同期処理について何かしらの誤解があるように見えますね…。
通常のWebの同期処理と非同期処理についてざっくりまとめると以下のようになります。

  • 通常のWebの処理(同期処理): ユーザからのリクエストに対して何かの処理を行い、それをユーザにレスポンスとして返す。返した内容がブラウザに表示される。
  • 非同期処理: ユーザのリクエストについての処理をレスポンスとして返さず、裏側で実行しておいて、別途何かしらの方法で伝える。

というわけで、「controllerが呼び出された際にブラウザ上に表示」するのであれば、同期処理として返した方が簡単です。

よくある非同期処理としては、ブラウザでは「処理を開始しました」とだけ返して、処理結果はメールで伝えるようなものですね。
もう少し凝ったものとしては、処理時間が長そうな場合、いったんブラウザでは「実行中…」的な文言を返しつつ、非同期処理の結果はWebSocket等で返す、といったものです。こちらはActionCableとかを使うことになりそうです。

どうして非同期処理をしたいのか、どういうふうに結果を返したいのかをもう少し考え直してみるとよいかもしれません。

投稿2018/07/26 16:05

編集2018/07/26 16:19
takahashim

総合スコア1877

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

FumiakiNakao

2018/07/26 22:41 編集

回答ありがとうございます。 おっしゃる通り、非同期処理の概念を誤解していたように思います。勉強しなおしてきます。 また、こちらの状況説明も足りませんでした。 最終的には、とあるAPIから10件程度のデータをうけとって、そのデータ1件1件を別のあるAPIに投げていき、それらすべての結果を即時表示するといったことをやりたいと考えています。 別APIからのレスポンスを受け取るのが、1件あたり2秒、合計で20秒ほどで、「実行中」というサークルを出すにも限界があるなとおもったので非同期で処理したいと考えていた次第です ActionCableという概念は今まで知らなかったので非常に参考になりました ありがとうございました
guest

0

自己解決

皆様回答ありがとうございました。

結局、非同期でおこないたい処理がhttp requestに限定されていたことから、typhoeusというgemを使って解決いたしました。

ですが、皆様の回答の中には自分が今まで使ったことのない機能があり、大変勉強になりました。今後参考にさせていただこうと思います.

投稿2018/08/05 06:29

FumiakiNakao

総合スコア180

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

非同期処理の結果をリアルタイムで受け取りたいという話でしたら、下記のような構成はいかがでしょうか?

実装内容は

クライアントサイド

・job実行リクエスト ・websocket(もしくはpolling)で非同期処理の結果の監視

サーバーサイド

・job実行リクエストの受付 ・jobの実行 ・jobの結果をwebsocket(もしくはpolling)で返す

jobの処理

・やりたい処理 ・jobの結果をデータベースに保存する

シーケンス的には

1. クライアント→サーバー (job実行リクエスト)
2. サーバー→job (jobの呼び出し・実行)
3. サーバー→クライアント (job実行リクエストのレスポンスaccepted

*********** websocket(もしくはpolling)で繰り返し
4. クライアント→サーバー(結果の取得リクエスト)
5. サーバー→クライアント(結果の取得リクエストのレスポンス)
***********

6. job→データベース (結果の登録)

非同期なので書くのが難しいですが、処理4、5を繰り返して、jobが更新するデータを返す。

投稿2018/07/27 01:34

satoshih

総合スコア797

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

重たい処理を、同期的っぽく処理しているように見せたいことはよくありますね。
Sidekiqだと終わったjobを消す運用の場合もあり、job_idを引き回したとしても上手にとれないことがあるので、perform_later自体の返り値を云々するのは、ちょっと難しいように思います。

実際のユースケースによるのですが、messageが永続化しても問題ない程度の「概念」を表しているのであれば、
messageをModel化してしまって、こんな感じの処理を書くことがあります。

rb

1 2class Message < ApplicationRecord 3 # finished:boolean default: false 4 # hello:text 5 # 的なモデル 6end 7 8class TestJob < ApplicationJob 9 def perform(message) 10 11 # ものすごく重たい処理 12 message.hello = 'world' 13 # ものすごく重たい処理 14 15 message.finished = true 16 message.save! 17 end 18end 19 20class TestController < ApplicationController 21 # GET /test 22 def index 23 @message = Message.create! 24 TestJob.perform_later(@message) 25 end 26 27 # GET /test/1 28 def show 29 @message = Message.find(params[:id]) 30 render json: @message 31 end 32end

test/index.html.erb

1<script> 2 3// 要jQuery 4// 動作確認はしてない。ニュアンスが伝われば 5var timer = setInterval(function(){ 6 $.getJSON('/test/<%= @message.id %>.json', function(data){ 7 if(data.finished) { 8 $('#result').html(data.hello); 9 clearInterval(timer); 10 } 11 }); 12}, 1000) 13</script> 14 15<pre id="result">now processing...</pre>

注意点として、Messageモデルはどんどん溜まってしまうので、定期実行でゴミを消すなどが必要でしょう。
(finishedをfinished_atにして、時間が経過していたら消してしまう、など)

投稿2018/07/26 16:45

takumiabe

総合スコア661

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

FumiakiNakao

2018/07/26 22:31

回答ありがとうございます 初心者的な質問で申し訳ないのですが、これは「`rails g model`などでモデル作成して、データをため込んでいく」というやり方ですかね? 最終的には、とあるAPIから10件程度のデータをうけとって、そのデータ1件1件を別のあるAPIに投げていき、それらすべての結果を表示するといったことをやりたいと考えています。 別APIからのレスポンスを受け取るのが、1件あたり2秒、合計で20秒ほどかかってしまうので、非同期処理にしようとしてる次第です この状況で、教えてくださった方法は適切ですか? 追加で答えてくださると幸いです よろしくお願いいたします!
takumiabe

2018/07/27 01:24

> 「`rails g model`などでモデル作成して、データをため込んでいく」 そのとおりです。 > この状況で、教えてくださった方法は適切ですか? その重さであれば、非同期処理にする他ないとは思います。 あとは、ユーザーが完了を知る必要がどれぐらい即座に必要か、というところです。 あらっぽくで良ければ、上記の1秒毎に簡易APIチェック、でも大丈夫でしょう。 ほぼリアルタイムに、であれば、他の方が示唆されておられるとおり、ActionCable / WebSocket のような、サーバーからのpush型のやりとりが必要になるかと思います。 たとえば、ajaxによる画像アップロードの実装を探して、参考にされるのも良いかもしれません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問