async — Kotlin中的协程构建器,启动一个协程并返回Deferred<T>对象以获取异步操作的结果。async允许并行执行多个任务并通过await()聚合其结果。根据JetBrains 2024指南,async是在协程中组织并行计算的首选方式。
要点
async — 是CoroutineScope的扩展函数,创建一个可返回结果的协程。与launch不同,async返回Deferred<T> — 一个表示未来值的对象。通过async启动的协程与其他协程并发执行,从而实现计算的并行化。
当异步操作的结果需要进一步处理时使用async:从多个来源加载数据、批处理集合、向不同API发出并行请求。Deferred会立即创建,结果在协程完成后变得可用。
import kotlinx.coroutines.*
suspend fun String.delayedValue(): String {
delay(1000L)
return this
}
fun main() = runBlocking {
val deferred: Deferred<String> = async { "Hello".delayedValue() }
println(deferred.await())
}
async的签名与launch相似:相同的CoroutineContext、CoroutineStart参数和suspend块。区别在于返回类型 — Deferred<T>代替Job,其中T是块中最后一个表达式返回的结果类型。
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
Deferred<T> — 继承Job并添加await()、getCompleted()、getCompletionExceptionOrNull()方法的接口。await()是挂起函数,只能在协程内部或其他挂起函数中调用。getCompleted() — 非阻塞方法,如果协程尚未完成则抛出异常。
async的主要优势是能够并行启动任务。不需要顺序等待每个操作(总时间=时间总和),async同时启动协程,将总时间减少到最长操作的时间。
典型场景 — 通过三个并行async请求加载用户个人资料、设置和订单历史记录。所有三个完成后,结果合并为单一数据模型。
suspend fun loadUserDashboard(userId: String): Dashboard = coroutineScope {
val profile = async { api.getProfile(userId) }
val settings = async { api.getSettings(userId) }
val orders = async { api.getRecentOrders(userId) }
Dashboard(
profile = profile.await(),
settings = settings.await(),
orders = orders.await()
)
}
coroutineScope函数启动子协程并在返回结果前等待它们完成。整个块并行执行,而非顺序执行。
Deferred支持多种处理结果的方法。除了await(),还有用于检查状态、获取就绪值和通过Kotlin协程工具函数聚合多个Deferred的方法。
| 方法 | 描述 | 是否挂起? |
|---|---|---|
| await() | 暂停协程直到获得结果 | 是 |
| getCompleted() | 如果协程完成则返回结果(否则抛出异常) | 否 |
| getCompletionExceptionOrNull() | 出错时返回异常或null | 否 |
| isCompleted | 检查协程是否完成 | 否 |
要聚合Deferred列表,使用awaitAll() — 一个暂停执行直到集合中所有Deferred完成的函数。如果至少有一个以错误结束,awaitAll()会抛出异常。
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
return coroutineScope {
val deferreds = requests.map { async { it() } }
deferreds.awaitAll()
}
}
选择async还是launch取决于是否需要协程执行的结果。launch返回Job,适用于即发即忘的操作;async返回Deferred,用于返回数据的任务。在语法和参数方面,构建器是相同的。
重要规则:切勿对即发即忘使用async。如果不需要协程的结果,请使用launch。async会带来创建Deferred的开销,在这种情况下是不合理的。
在Android中,async用于ViewModel和UseCases内部的并行操作。典型场景:同时加载个人资料和新闻源、从多个气象服务获取天气、与服务器进行批量数据同步。
class HomeViewModel : ViewModel() {
fun loadHomeScreen() {
viewModelScope.launch {
val userData = async(Dispatchers.IO) { repository.getUserData() }
val newsFeed = async(Dispatchers.IO) { repository.getNewsFeed() }
val notifications = async(Dispatchers.IO) { repository.getNotifications() }
val state = HomeState(
user = userData.await(),
news = newsFeed.await(),
unread = notifications.await()
)
_uiState.update { state }
}
}
}
建议将async组包装在coroutineScope或supervisorScope中以隔离错误。即使其中一个async协程出现错误,supervisorScope也允许其他async协程继续执行 — 这对于非关键操作很有用。
常见问题
可以,但没有意义。没有await()的async会启动协程,但其结果会丢失。如果不需要结果,请使用launch — 它不会创建Deferred,在内存和性能方面更高效。
async内部的异常保存在Deferred中。调用await()会传播此异常。要检查而不传播,请使用getCompletionExceptionOrNull()。使用coroutineScope时,错误会取消所有子协程。
awaitAll() — Iterable<Deferred<T>>的扩展函数,暂停协程直到所有Deferred完成。返回结果列表。如果至少一个Deferred以错误结束,则传播异常并取消其余部分。
有区别。async是CoroutineScope的扩展函数,绑定到作用域。GlobalScope.async创建不绑定生命周期的协程 — 可能无限运行。在Android中,不推荐使用GlobalScope,因为存在内存泄漏风险。
数量由调度器决定:Dispatchers.IO支持最多64个并发线程,Dispatchers.Default — 根据CPU核心数。对于批量操作,使用通过Semaphore或mapNotNull与awaitAll实现的有限并行性。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。