65
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

ActiveJobのperform_laterとperform_nowの違い

はじめに

ActiveJobのperformメソッドは、perform_laterperform_nowの2通りで呼び出すことができます(以下のコード例はRailsのAPIドキュメントから引用しました)。

class ProcessPhotoJob < ActiveJob::Base
  def perform(photo)
    photo.watermark!('Rails')
    photo.rotate!(90.degrees)
    photo.resize_to_fit!(300, 300)
    photo.upload!
  end
end

# perform_laterで呼び出す
ProcessPhotoJob.perform_later(photo)

# perform_nowで呼び出す
ProcessPhotoJob.perform_now(photo)

では、perform_laterperform_nowは具体的にどのような違いがあるのでしょうか?

ネットを検索してみたところ、意外と明確に説明してくれている資料がなかったので、独自にまとめてみることにします。

perform_laterとperform_nowの違いは何か

APIドキュメントには以下のような説明があります。

  • perform_laterは)ジョブをキューに入れ、キューが空き次第ジョブを実行する。(To enqueue a job to be performed as soon as the queuing system is free)
  • perform_nowは)キューに入ることなく即座に実行される。(A job can also be processed immediately without sending to the queue)

もう少し簡単にいうと、perform_laterは非同期的に実行され、perform_nowは同期的に実行されます。

具体的にどういう挙動の違いが現れるのか、次の項で確認していきます。

簡単なサンプルで比較してみる

perform_laterperform_nowの違いを確認するために、簡単なサンプルプログラムを作ってみました。(Railsのバージョンは5.2.3)

app/jobs/sample_job.rb
class SampleJob < ApplicationJob
  queue_as :default

  def perform(msg)
    puts "[JOB] start: #{msg}"
    # sleepを入れて時間がかかる処理であることをシミュレート
    sleep 3
    puts "[JOB] end: #{msg}"
  end
end
app/models/foo.rb
class Foo
  # 非同期的にSampleJobを呼び出すメソッド
  def execute_later
    puts '[RUNNER] execute_later start'
    SampleJob.perform_later('hello')
    puts '[RUNNER] execute_later end'
  end

  # 同期的にSampleJobを呼び出すメソッド
  def execute_now
    puts '[RUNNER] execute_now start'
    SampleJob.perform_now('nihao')
    puts '[RUNNER] execute_now end'
  end
end

perform_laterを呼び出した場合

perform_laterを呼び出した場合は次のような実行結果になります。

> foo = Foo.new
> foo.execute_later
[RUNNER] execute_later start
Enqueued SampleJob (Job ID: e6859c49-4ec4-41d2-820f-eef63615cc51) to Async(default) with arguments: "hello"
Performing SampleJob (Job ID: e6859c49-4ec4-41d2-820f-eef63615cc51) from Async(default) with arguments: "hello"
[RUNNER] execute_later end
[JOB] start: hello
[JOB] end: hello
Performed SampleJob (Job ID: e6859c49-4ec4-41d2-820f-eef63615cc51) from Async(default) in 3005.48ms

ご覧のとおり、Foo#execute_laterの実行が完了("[RUNNER] execute_later end"を表示)したあとに、SampleJob#performが実行されていることがわかります(非同期実行)。
そのため、Foo#execute_laterの呼び出し自体はすぐに完了します。

perform_nowを呼び出した場合

一方、perform_nowを呼び出した場合は次のような実行結果になります。

> foo = Foo.new
> foo.execute_now
[RUNNER] execute_now start
Performing SampleJob (Job ID: 9cd85c4c-e515-4d70-aeb7-ac915d7e745b) from Async(default) with arguments: "nihao"
[JOB] start: nihao
[JOB] end: nihao
Performed SampleJob (Job ID: 9cd85c4c-e515-4d70-aeb7-ac915d7e745b) from Async(default) in 3001.38ms
[RUNNER] execute_now end

こちらはFoo#execute_now内でSampleJob#performが即座に実行され、SampleJob#performの処理が完了してから、"[RUNNER] execute_now end"が出力されています(同期実行)。
SampleJob#performは3秒間スリープするようになっているため、Foo#execute_nowの実行時間も3秒以上かかることになります。

どう使い分けるのがよいか

ここまで説明した通り、ActiveJobではperform_laterperform_nowの2通りの呼び出し方があります。
では、この2種類のメソッドはどう使い分けるのがよいでしょうか?

基本的なユースケースではperform_laterを使うことが大半だと思います。
なぜなら、重たい処理(=時間のかかる処理)を非同期的に実行することが、ActiveJobを利用する主な動機になるはずだからです。

ですので、perform_nowはあまり使われないと思いますが、以下のようなユースケースでは例外的にperform_nowが必要になるかもしれません。

  • 通常は非同期実行するが、ある特定の条件下においては同期実行したい
  • rails consoleなどで、とりあえずジョブの動作確認がしたい

少なくとも、ActiveJobを作ったのにperform_nowでしか呼びだしていないようなコードがあれば、設計か実装に何か間違いがあるのかもしれません。

まとめ

というわけで、この記事ではActiveJobのperform_laterperform_nowの違いについて調べてみました。

簡単にまとめると、

  • perform_laterは非同期実行(呼び出し側はジョブの完了を待たずに先へ進む)
  • perform_nowは同期実行(呼び出し側はジョブの完了を待ち続ける)

ということになります。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
65
Help us understand the problem. What are the problem?