
# @Async

`@Async` runs an `@EventListener` method on a `TaskExecutor` instead of inside the publishing call.

## Why use it

By default a listener runs inside `publishEvent`, so the publisher waits until it is done. That is wrong for slow work such as sending a mail or calling a remote service: placing an order should not wait for the mail. With `@Async` the listener runs later, and `publishEvent` returns at once.

- It takes effect only when the context has a `TaskExecutor` bean. This is how it is switched on, as `@EnableAsync` does in Spring. The scheduling starter (`spring_boot_starter_scheduling`) registers one, named `applicationTaskExecutor` with the alias `taskExecutor`. Without any executor, `@Async` is ignored and the listener runs synchronously.
- `@Async('name')` picks an executor bean by name. Without a name, the context uses the only (or `@Primary`) `TaskExecutor` bean, else the one named `taskExecutor`.
- A failure (thrown, or from the returned `Future`) goes to the multicaster's error handler, else it is logged.
- A result is published as an event when it arrives, as for synchronous listeners.

Dart has no threads here. `SimpleAsyncTaskExecutor` runs the task in a later turn of the event loop of the same isolate. A long synchronous task still blocks everything else.

To make every listener asynchronous instead of one method, declare a `SimpleApplicationEventMulticaster(taskExecutor: ...)` bean named `applicationEventMulticaster` (see the second example).

## Example

```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 {
  // Having a TaskExecutor bean is what switches @Async on.
  // (spring_boot_starter_scheduling registers one for you.)
  @Bean()
  TaskExecutor taskExecutor() => SimpleAsyncTaskExecutor();
}

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

  // The order is confirmed without the publisher waiting for the mail.
  @EventListener()
  @Async()
  Future<void> onPlaced(OrderPlaced order) =>
      mail.send(order.customer, 'Order ${order.id} confirmed');
}
```

All listeners asynchronous, with errors logged instead of thrown:

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

@Configuration()
class EventConfig {
  // The bean name `applicationEventMulticaster` replaces the context's own.
  @Bean()
  ApplicationEventMulticaster applicationEventMulticaster() =>
      SimpleApplicationEventMulticaster(
        taskExecutor: SimpleAsyncTaskExecutor(),
        errorHandler: (error, stack) =>
            log.error('listener failed', error: error, stackTrace: stack),
      );
}
```

In tests, `SimpleAsyncTaskExecutor.whenIdle` completes once no task is pending. Await it before you check what an asynchronous listener did.

## Parameters

| Name | Type | Default | Meaning |
|---|---|---|---|
| `value` | `String?` | `null` | Name of the `TaskExecutor` bean. `null` uses the default one. |

## In Spring (Java)

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

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

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

Differences that matter:

- There is no `@EnableAsync`. A `TaskExecutor` bean switches `@Async` on.
- `@Async` is accepted on `@EventListener` methods only. Spring puts it on any bean method through a runtime proxy, and Dart has no runtime proxies. The generator rejects it elsewhere.
- Tasks run in a later event-loop turn of the same isolate, not on another thread.

## See also

- [@EventListener](event-listener.md)
- [Events](index.md)
- [@Bean](../beans/bean.md)
- [@Configuration](../beans/configuration.md)
