async:什么是它,Deferred及并行协程如何工作

作者: IT Sectr 发布日期: 2026-06-22 阅读时间: 8 分钟

async — Kotlin中的协程构建器,启动一个协程并返回Deferred<T>对象以获取异步操作的结果。async允许并行执行多个任务并通过await()聚合其结果。根据JetBrains 2024指南,async是在协程中组织并行计算的首选方式。

要点

  • async — 协程构建器,返回带异步操作结果的Deferred<T>
  • Deferred — 异步承诺,继承Job,具有await()方法获取结果
  • async允许并行执行独立任务,缩短总时间
  • await() — 挂起函数,在不阻塞的情况下暂停协程直到获得结果
  • 结构化并发与async确保在出错时取消所有并行任务

Kotlin协程中的async是什么?

async — 是CoroutineScope的扩展函数,创建一个可返回结果的协程。与launch不同,async返回Deferred<T> — 一个表示未来值的对象。通过async启动的协程与其他协程并发执行,从而实现计算的并行化。

当异步操作的结果需要进一步处理时使用async:从多个来源加载数据、批处理集合、向不同API发出并行请求。Deferred会立即创建,结果在协程完成后变得可用。

kotlin
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和Deferred的语法

async的签名与launch相似:相同的CoroutineContext、CoroutineStart参数和suspend块。区别在于返回类型 — Deferred<T>代替Job,其中T是块中最后一个表达式返回的结果类型。

kotlin
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同时启动协程,将总时间减少到最长操作的时间。

并行数据加载

典型场景 — 通过三个并行async请求加载用户个人资料、设置和订单历史记录。所有三个完成后,结果合并为单一数据模型。

kotlin
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结果

Deferred支持多种处理结果的方法。除了await(),还有用于检查状态、获取就绪值和通过Kotlin协程工具函数聚合多个Deferred的方法。

方法描述是否挂起?
await()暂停协程直到获得结果
getCompleted()如果协程完成则返回结果(否则抛出异常)
getCompletionExceptionOrNull()出错时返回异常或null
isCompleted检查协程是否完成

要聚合Deferred列表,使用awaitAll() — 一个暂停执行直到集合中所有Deferred完成的函数。如果至少有一个以错误结束,awaitAll()会抛出异常。

kotlin
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
    return coroutineScope {
        val deferreds = requests.map { async { it() } }
        deferreds.awaitAll()
    }
}

async与launch的区别

选择async还是launch取决于是否需要协程执行的结果。launch返回Job,适用于即发即忘的操作;async返回Deferred,用于返回数据的任务。在语法和参数方面,构建器是相同的。

  • launch → Job(无结果),async → Deferred<T>(有结果T)
  • launch — 即发即忘任务(日志记录、缓存、发送分析数据)
  • async — 并行计算(从多个API加载、批处理)
  • 两者都支持结构化并发和CoroutineStart参数
  • await()仅适用于Deferred,launch不提供结果

重要规则:切勿对即发即忘使用async。如果不需要协程的结果,请使用launch。async会带来创建Deferred的开销,在这种情况下是不合理的。

移动开发中的async示例

在Android中,async用于ViewModel和UseCases内部的并行操作。典型场景:同时加载个人资料和新闻源、从多个气象服务获取天气、与服务器进行批量数据同步。

kotlin
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?

可以,但没有意义。没有await()的async会启动协程,但其结果会丢失。如果不需要结果,请使用launch — 它不会创建Deferred,在内存和性能方面更高效。

async如何处理异常?

async内部的异常保存在Deferred中。调用await()会传播此异常。要检查而不传播,请使用getCompletionExceptionOrNull()。使用coroutineScope时,错误会取消所有子协程。

Kotlin协程中的awaitAll是什么?

awaitAll() — Iterable<Deferred<T>>的扩展函数,暂停协程直到所有Deferred完成。返回结果列表。如果至少一个Deferred以错误结束,则传播异常并取消其余部分。

async与GlobalScope.async有何不同?

有区别。async是CoroutineScope的扩展函数,绑定到作用域。GlobalScope.async创建不绑定生命周期的协程 — 可能无限运行。在Android中,不推荐使用GlobalScope,因为存在内存泄漏风险。

可以启动多少个async协程用于并行请求?

数量由调度器决定:Dispatchers.IO支持最多64个并发线程,Dispatchers.Default — 根据CPU核心数。对于批量操作,使用通过Semaphore或mapNotNull与awaitAll实现的有限并行性。

总结

  • async — 协程构建器,返回Deferred<T>以异步获取结果
  • Deferred继承Job并添加await()、getCompleted()、getCompletionExceptionOrNull()方法
  • 并行执行使用async将总时间减少到最长操作的持续时间
  • awaitAll() — 等待集合中所有Deferred的工具
  • coroutineScope — 推荐的async组包装器,具有取消保证
  • 不要对即发即忘使用async — 请使用launch
  • 在Android中,async用于ViewModel中使用viewModelScope的并行加载

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读