ActiveJobのperform_laterとperform_nowの違い

## はじめに
ActiveJobのperformメソッドは、`perform_later`と`perform_now`の2通りで呼び出すことができます（以下のコード例は[RailsのAPIドキュメント](https://edgeapi.rubyonrails.org/classes/ActiveJob/Base.html)から引用しました）。

```ruby
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_later`と`perform_now`は具体的にどのような違いがあるのでしょうか？

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

## perform_laterとperform_nowの違いは何か

[APIドキュメント](https://edgeapi.rubyonrails.org/classes/ActiveJob/Base.html)には以下のような説明があります。

- （`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_later`と`perform_now`の違いを確認するために、簡単なサンプルプログラムを作ってみました。（Railsのバージョンは5.2.3）

```ruby: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
```

```ruby: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_later`と`perform_now`の2通りの呼び出し方があります。
では、この2種類のメソッドはどう使い分けるのがよいでしょうか？

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

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

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

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

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

簡単にまとめると、

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

ということになります。