@Async

@Async は、@EventListener メソッドを発行の呼び出しの中ではなく、TaskExecutor 上で実行します。

使う理由

デフォルトでは、リスナーは publishEvent の中で実行されるため、発行側は処理が終わるまで待ちます。メール送信やリモートサービスの呼び出しのような遅い処理では、これは望ましくありません。注文の確定がメール送信を待つべきではないからです。@Async を付けるとリスナーは後で実行され、publishEvent はすぐに戻ります。

ここでの Dart にはスレッドがありません。SimpleAsyncTaskExecutor は、同じアイソレートのイベントループの後のターンでタスクを実行します。長い同期処理は、やはり他のすべてを止めてしまいます。

一つのメソッドではなくすべてのリスナーを非同期にするには、applicationEventMulticaster という名前で SimpleApplicationEventMulticaster(taskExecutor: ...) の Bean を宣言します (2 番目の例を参照)。

例

import 'package:spring_core_dart/spring_core_dart.dart';

class OrderPlaced {
  const OrderPlaced(this.id, this.customer);
  final String id;
  final String customer;
}

abstract interface class MailSender {
  Future<void> send(String to, String subject);
}

@Configuration()
class AsyncConfig {
  // TaskExecutor の Bean があることで @Async が有効になる。
  // (spring_boot_starter_scheduling は自動で一つ登録する。)
  @Bean()
  TaskExecutor taskExecutor() => SimpleAsyncTaskExecutor();
}

@Component()
class Mailer {
  Mailer(this.mail);
  final MailSender mail;

  // 発行側はメール送信を待たずに注文を確定できる。
  @EventListener()
  @Async()
  Future<void> onPlaced(OrderPlaced order) =>
      mail.send(order.customer, 'Order ${order.id} confirmed');
}

すべてのリスナーを非同期にし、エラーは投げずにログに出す例:

const log = SpringLog('neon.events');

@Configuration()
class EventConfig {
  // `applicationEventMulticaster` という Bean 名でコンテキスト自身のものを置き換える。
  @Bean()
  ApplicationEventMulticaster applicationEventMulticaster() =>
      SimpleApplicationEventMulticaster(
        taskExecutor: SimpleAsyncTaskExecutor(),
        errorHandler: (error, stack) =>
            log.error('listener failed', error: error, stackTrace: stack),
      );
}

テストでは、SimpleAsyncTaskExecutor.whenIdle は保留中のタスクがなくなると完了します。非同期リスナーの結果を確認する前に await してください。

パラメーター

名前 型 デフォルト 意味
value String? null TaskExecutor の Bean 名。null ならデフォルトのものを使う。

Spring (Java) との対応

@EnableAsync
@Configuration
class AsyncConfig {}

@Component
class Mailer {
  @Async
  @EventListener
  CompletableFuture<Void> onPlaced(OrderPlaced order) { ... }

  @Async("mailExecutor")
  @EventListener
  void onShipped(OrderShipped order) { ... }
}

重要な違い:

関連項目