@Async 异步执行
@Async 是 ACE Framework 提供的异步执行切面宏。标注在 @Service 的方法上后,框架会在独立子协程中执行该方法,调用方立即获得一个 Future<T> 句柄,可按需等待结果或继续执行其他逻辑。
快速上手
cangjie
package demo.service
import ace.framework.*
@Service
class ExternalClient {
// 调用外部 API A,在子协程中执行
@Async
func fetchFromServiceA(id: Int64): String {
// 模拟 I/O 耗时
Thread.sleep(Duration.millisecond * 200)
return "result-A-${id}"
}
// 调用外部 API B,在子协程中执行
@Async
func fetchFromServiceB(id: Int64): String {
Thread.sleep(Duration.millisecond * 150)
return "result-B-${id}"
}
}@Async 方法的实际返回类型在宏展开后变为 Future<T>(原返回类型 T 被包裹),调用方无需手动创建协程。
并行调用示例
cangjie
package demo.controller
import ace.framework.*
@Controller["/api"]
class AggregateController {
@Inject
var client!: ExternalClient
@Get["/aggregate/:id"]
func aggregate(@PathParam id: Int64): String {
// 两个子协程并行发起,不互相阻塞
let futureA = client.fetchFromServiceA(id)
let futureB = client.fetchFromServiceB(id)
// 等待两个结果(总耗时 ≈ max(200, 150) = 200 ms,而非 350 ms)
let a = futureA.get()
let b = futureB.get()
return "${a} | ${b}"
}
}API 说明
| 成员 | 类型 | 说明 |
|---|---|---|
future.get() | T | 阻塞直到协程完成,返回结果;若抛出异常则重新抛出 |
future.tryGet() | Option<T> | 非阻塞轮询,未完成返回 None,已完成返回 Some(value) |
future.getOrDefault(default) | T | 阻塞等待,若抛异常则返回 default |
future.isCompleted() | Bool | 查询协程是否已结束 |
tryGet 轮询示例
cangjie
let future = client.fetchFromServiceA(42)
// 主协程做其他工作,偶尔检查
var result: Option<String> = None
while (result.isNone()) {
doOtherWork()
result = future.tryGet()
}
println(result.getOrThrow())注意事项
不能与 @Transactional 混用
@Async 会在新协程中执行,而事务上下文绑定在原协程上。若方法同时标注 @Transactional,子协程内的数据库操作将在事务上下文之外执行,导致数据不一致。正确做法:在调用 @Async 方法之前完成事务操作,或将事务与异步逻辑分拆到不同方法。
适用场景
- 并行调用多个外部 HTTP/RPC 服务
- 异步发送邮件、消息推送等"发完不管"任务
- 预热缓存、异步写日志等副作用操作
异常不会自动传播
子协程抛出的异常不会自动打印,只有在调用 future.get() 时才会重新抛出。"发完不管"模式下,请在方法内部 try/catch 并记录日志,否则异常将被静默丢弃。
与普通协程对比
| 方式 | 代码侵入 | 返回值 | 适用场景 |
|---|---|---|---|
@Async 宏 | 无(只加注解) | Future<T> | Service 方法并行化 |
手动 spawn { } | 有(改动调用方) | Future<T> | 细粒度控制协程生命周期 |
Thread.sleep + 手动同步 | 高 | 自行管理 | 底层特殊需求 |