特性 | launch |
async |
---|---|---|
返回类型 | Job |
Deferred<T> |
主要用途 | 执行一个独立的后台任务,通常不关心其执行结果,即“即发即忘”(Fire and Forget)。 | 执行一个并发任务,并期望在未来获得一个类型为 T 的结果。 |
结果获取 | Job 对象本身不携带返回值。可以通过 job.join() 挂起并等待任务执行完成。 |
通过返回的 Deferred<T> 对象调用其 await() 方法,可以挂起当前协程并等待结果 T 。 |
异常处理 | 自动传播异常。如果协程内部发生未捕获的异常,它会立即在 CoroutineScope 中传播,通常会导致应用崩溃(遵循结构化并发的失败传递原则)。 |
延迟处理异常。异常会被“包裹”在 Deferred 对象中,直到调用 await() 时才会抛出。如果不调用 await() ,异常将被静默忽略,这是一个常见的陷阱。 |
注:Deferred<T>
继承自 Job
,因此 async
返回的对象也拥有 Job
的所有功能(如 cancel()
, join()
等)。
launch
?当你需要执行一段代码,但并不需要其返回一个明确的结果时,launch
是最佳选择。它适用于独立的、一次性的操作。
SharedPreferences
写入数据。示例代码:
import kotlinx.coroutines.*
fun main() = runBlocking {
println("主程序开始")
// 启动一个“即发即忘”的协程任务
val job: Job = launch {
delay(1000L) // 模拟耗时操作
println("后台任务执行完毕。")
}
println("主程序继续执行,无需等待 launch 完成。")
// 如果需要,也可以等待 job 完成
// job.join()
delay(1500L) // 保持主程序存活足够长的时间以看到 launch 的输出
println("主程序结束")
}
async
?当你需要执行一个或多个并发任务,并且需要利用它们的返回值来进行下一步计算或处理时,async
是不二之选。它非常适合并行执行任务以缩短总耗时。
示例代码:
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
// 模拟从网络获取用户数据
suspend fun fetchUserData(): String {
delay(1000L)
return "User Data"
}
// 模拟从网络获取新闻数据
suspend fun fetchNewsData(): String {
delay(1500L)
return "News Data"
}
fun main() = runBlocking {
println("开始并发获取数据...")
val totalTime = measureTimeMillis {
// 使用 async 并发启动两个网络请求
val userDataDeferred: Deferred<String> = async { fetchUserData() }
val newsDataDeferred: Deferred<String> = async { fetchNewsData() }
// 等待两个任务的结果
val (userData, newsData) = awaitAll(userDataDeferred, newsDataDeferred)
println("数据获取成功: $userData, $newsData")
}
// 总耗时约等于最长的那个任务的耗时 (1500ms),而不是两者之和
println("总耗时: ${totalTime}ms")
}
选择 launch
还是 async
的核心依据非常简单:“我是否需要这个任务的返回值?”
launch
。async
。