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

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

ただいまの
回答率

90.35%

  • Ruby

    8157questions

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

  • Ruby on Rails 5

    2137questions

  • Redis

    110questions

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

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

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 596

FumiakiNakao

score 140

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

現状は以下の通りです。

  • job
#app/jobs/test_job.rb 
   class TestJob < ApplicationJob                                                                                                                        
     queue_as :default                                                                                                                                       

     def perform(**message)                                                                                                                                                                                                                                                            
       message["hello"]="world"                                                                                                                              
       return message                                                                                                                                        
     end                                                                                                                                                     
   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秒ほどかかってしまうので、非同期処理にしようとしてる次第です

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

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

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

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

+2

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

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

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

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

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

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/27 07:37 編集

    回答ありがとうございます。
    おっしゃる通り、非同期処理の概念を誤解していたように思います。勉強しなおしてきます。

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

    ActionCableという概念は今まで知らなかったので非常に参考になりました ありがとうございました

    キャンセル

check解決した方法

0

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

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

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

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

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

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

class Message < ApplicationRecord
  # finished:boolean default: false
  # hello:text
  # 的なモデル
end

class TestJob < ApplicationJob
  def perform(message)

    # ものすごく重たい処理
    message.hello = 'world'
    # ものすごく重たい処理

    message.finished = true
    message.save!
  end
end

class TestController < ApplicationController
  # GET /test
  def index
    @message = Message.create!
    TestJob.perform_later(@message)
  end

  # GET /test/1
  def show
    @message = Message.find(params[:id])
    render json: @message
  end
end  
<script>

// 要jQuery
// 動作確認はしてない。ニュアンスが伝われば
var timer = setInterval(function(){
  $.getJSON('/test/<%= @message.id %>.json', function(data){
    if(data.finished) {
      $('#result').html(data.hello);
      clearInterval(timer);
    }
  });
}, 1000)
</script>

<pre id="result">now processing...</pre>

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

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

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

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/07/27 07:31

    回答ありがとうございます

    初心者的な質問で申し訳ないのですが、これは「`rails g model`などでモデル作成して、データをため込んでいく」というやり方ですかね?

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

    この状況で、教えてくださった方法は適切ですか?

    追加で答えてくださると幸いです よろしくお願いいたします!

    キャンセル

  • 2018/07/27 10:24

    > 「`rails g model`などでモデル作成して、データをため込んでいく」
    そのとおりです。

    > この状況で、教えてくださった方法は適切ですか?

    その重さであれば、非同期処理にする他ないとは思います。

    あとは、ユーザーが完了を知る必要がどれぐらい即座に必要か、というところです。

    あらっぽくで良ければ、上記の1秒毎に簡易APIチェック、でも大丈夫でしょう。
    ほぼリアルタイムに、であれば、他の方が示唆されておられるとおり、ActionCable / WebSocket のような、サーバーからのpush型のやりとりが必要になるかと思います。

    たとえば、ajaxによる画像アップロードの実装を探して、参考にされるのも良いかもしれません。

    キャンセル

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が更新するデータを返す。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

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

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

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • Ruby

    8157questions

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

  • Ruby on Rails 5

    2137questions

  • Redis

    110questions

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