
# @Async

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

## 使う理由

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

- コンテキストに `TaskExecutor` の Bean があるときだけ有効になります。Spring の `@EnableAsync` に相当する有効化の方法です。スケジューリングのスターター (`spring_boot_starter_scheduling`) は、`applicationTaskExecutor` という名前 (別名 `taskExecutor`) で一つ登録します。エグゼキューターが一つもなければ `@Async` は無視され、リスナーは同期的に実行されます。
- `@Async('name')` は、名前でエグゼキューターの Bean を選びます。名前がなければ、唯一の (または `@Primary` の) `TaskExecutor` の Bean、それもなければ `taskExecutor` という名前の Bean を使います。
- 失敗 (例外、または返した `Future` のエラー) は、マルチキャスターのエラーハンドラーに渡されます。ハンドラーがなければログに出力されます。
- 結果は、同期リスナーと同じく、得られた時点でイベントとして発行されます。

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

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

## 例

```dart
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');
}
```

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

```dart
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) との対応

```java
@EnableAsync
@Configuration
class AsyncConfig {}

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

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

重要な違い:

- `@EnableAsync` はありません。`TaskExecutor` の Bean が `@Async` を有効にします。
- `@Async` は `@EventListener` メソッドにしか付けられません。Spring は実行時プロキシを使って任意の Bean メソッドに付けられますが、Dart には実行時プロキシがありません。それ以外の場所に付けるとジェネレーターがエラーにします。
- タスクは別スレッドではなく、同じアイソレートのイベントループの後のターンで実行されます。

## 関連項目

- [@EventListener](event-listener.md)
- [イベント](index.md)
- [@Bean](../beans/bean.md)
- [@Configuration](../beans/configuration.md)
