launch 与 async
概述
在 Kotlin 协程中,launch 和 async 是两种最常用的协程构建器(Coroutine Builder),用于启动协程并执行异步任务。两者虽然功能相似,但在使用场景和返回值处理上有显著区别。
1. launch 构建器
特点
- 用途:用于启动一个不需要返回结果的协程(即“发射后不管”模式)。
- 返回值:返回一个
Job对象,用于控制协程的生命周期(如取消、等待完成等)。 - 异常处理:未捕获的异常会传递给父协程或全局异常处理器。
语法示例
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
// 执行异步任务(无返回值)
delay(1000)
println("Task completed")
}
println("Main thread continues")
job.join() // 等待协程完成
}
典型场景
- 执行后台任务(如日志记录、网络请求的副作用操作)。
- 不需要返回值的异步操作。
2. async 构建器
特点
- 用途:用于启动需要返回结果的协程。
- 返回值:返回一个
Deferred<T>对象(类似Future),可通过await()获取结果。 - 异常处理:异常不会立即抛出,而是在调用
await()时抛出。
语法示例
import kotlinx.coroutines.*
fun main() = runBlocking {
val deferred = async {
// 执行异步任务并返回结果
delay(1000)
"Result"
}
println("Main thread continues")
val result = deferred.await() // 获取结果(会挂起当前协程)
println("Result: $result")
}
典型场景
- 并行计算或并发请求(如同时调用多个 API)。
- 需要组合多个异步结果的场景。
3. launch vs async 对比
| 特性 | launch | async |
|---|---|---|
| 返回值 | Job(无结果) | Deferred<T>(有结果) |
| 异常处理 | 立即抛出 | 延迟到 await() 时抛出 |
| 使用场景 | 不需要返回值的任务 | 需要返回值的任务 |
| 是否阻塞 | 非阻塞 | 调用 await() 时会挂起协程 |
4. 组合使用示例
通过 async 实现并行任务:
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
fun main() = runBlocking {
val time = measureTimeMillis {
val deferred1 = async { fetchData(1, 1000) }
val deferred2 = async { fetchData(2, 1500) }
println("Result: ${deferred1.await() + deferred2.await()}")
}
println("Total time: $time ms") // 约 1500ms(并行执行)
}
suspend fun fetchData(id: Int, delayMs: Long): Int {
delay(delayMs)
return id * 10
}
5. 注意事项
- 结构化并发:始终在协程作用域(如
coroutineScope或runBlocking)内使用launch/async,避免协程泄漏。 - 异常传播:
async的异常需通过try-catch包裹await()处理。 - 取消操作:通过
Job.cancel()或Deferred.cancel()取消协程。
总结
- 使用
launch处理“无需结果”的任务,用async处理“需要结果”的任务。 - 通过
Deferred.await()实现协程间的同步和数据传递。 - 结合结构化并发和异常处理,编写健壮的异步代码。
