もう一つの非同期、Queueable

非同期でApexは@futureメソッドを使用した方法がスタンダードですが、今回は割と最近出たキュー可能Apexを使った方法について取り上げたいと思います。
余談だけど、キューって英語で書くと「きゅーうえうえ」って書くからなんか違和感あるよね。

スポンサーリンク

使用方法

キュー可能なApexは@futureと同じ、非同期で実行されるApexクラスとなります。
使用方法としては、Queueableインタフェースを使用するだけです。
Queueableインタフェースを使用するとexecuteメソッドが必須となるので、executeメソッド内に非同期処理するコードを記述します。

実行する場合は、System.enqueueJobメソッドを使用して、引数にQueueableインタフェースを実装したクラスを渡すことで、非同期でexecuteが実施されます。

@futureとの違いは?

でも、@futureアノテーションでの非同期実行があるのに、何でキュー可能Apexを使う必要があるのでしょうか?
@futureより優れている点は以下の3つです。

様々な形の引数が使用できる

@futureメソッドでの非同期実行でも引数を渡せますが、渡せる引数はプリミティブ型(IntegerやStringなど)かそのリスト型という単純なものしか使用できませんでした。
キュー可能Apexはexecuteに直接パラメータを渡すことはできませんが、コンストラクタでパラメータを渡すことが可能で、sObject型やクラスなどの複雑なパラメータも渡すことが可能です。

ジョブ監視ができる

System.enqueueJobメソッドで実行した時に、AsyncApexJobのレコードIDを返却します。
実施中は非同期ですが、このレコードIDを使って実行状況を追跡することができます。

以下SOQLを実行することで、ジョブの監視ができます。
JobTypeには「Queueable」が入り、Statusには「Failed」や「Completed」などの実行状況を表すステータスが入ります。

ジョブ監視用のSOQL

Select Id, JobType, Status From AsyncApexJob Where Id = 'レコードID'

チェーニングができる

これが、一番の特徴かもしれません。
executeメソッド内で、さらにSystem.enqueueJobメソッドを使用して、キュー可能Apexからキュー可能Apexを実行することができます。
これをチェーニングと呼びます。
ーニングでは無いので注意!
鎖のチェーンから来てるのでチェーニングですね。

コード例

実際にコードを記述しました。チェーニングを使って実現します。

まずは、最初に呼ばれるApex。
executeの中でやる処理は適当です。

FirstJob.cls

public class FirstJob implements Queueable {
	public void execute(QueueableContext context) {
        for(Integer i = 0; i < 100; i++){
	        System.debug('Hoge First');
        }
        System.enqueueJob(new SecondJob());
    }
}

次に、SecondJob。こちらは、Httpコールアウトを使用しています。

SecondJob.cls

public class SecondJob implements Queueable,Database.AllowsCallouts {
	public void execute(QueueableContext context) {
        for(Integer i = 0; i < 10; i++){
	        //HTTPリクエストの作成
            HttpRequest req = new HttpRequest();
            req.setEndpoint('http://www.apexdevnet.com');
            req.setMethod('GET');
             
            //HTTPリクエストの送信
            Http http = new Http();
            HttpResponse res = http.send(req);
             
            //レスポンスチェック
            if (res.getStatusCode() == 200) {
                //成功時に実行したい処理
            } else {
                System.debug('Callout failed: ' + res);
            }
        }
    }
}

キュー可能Apexもバッチ同様、コールアウトを使用する時は、Database.AllowsCalloutsインタフェースが必要なので、使う場合は忘れずに。

テストクラスの書き方

テストクラスを記述する場合は、バッチクラス同様、Test.startTest()とTest.stopTest()の間にSystem.enqueueJobメソッドを挟めばOK。

FirstJobTest.cls

@isTest
private class FirstJobTest {
	@IsTest
    static void jobTest() {
        Test.startTest();
        System.enqueueJob(new FirstJob());
        Test.stopTest();
    }
}

FirstJobのテストクラスはこのように書けばOK・・・ではないんです。

このテストクラスを実行するとこんなエラーが発生します。

System.AsyncException: 最大スタック深度に達しました。

テストクラスでは、チェーニングができないんですね

このエラーを回避するには、テスト実行時にはチェーニングしないようにする制御が必要となります。
コードでは、Test.isRunningTest()を使用して、テスト実行時はチェーニングを回避するようにします。

FirstJob.clsのチェーニング部分

System.enqueueJob(new SecondJob());

これを、このように修正します。

if(!Test.isRunningTest()){
    System.enqueueJob(new SecondJob());
}

テストの時に、チェーニングを飛ばしているので、カバレッジは100%にはなりません。

まとめ

  • キュー可能ApexはQueueableインタフェースを使用
  • 複雑な引数、ジョブ監視、チェーニングが使用可能のため、@futureメソッドより用途が広い
  • テストクラス内でのチェーニングは使用不可

コメント