做 Android 开发时,经常要同时拉取用户信息、订单列表和未读消息数——三个接口,彼此独立,又得一起展示。以前用 Retrofit 配合 Callback 或 LiveData 拼接数据,写一堆回调嵌套或状态管理逻辑,一不小心就掉进“回调地狱”。现在 Kotlin 协程 + zip,三行代码搞定合并请求,干净利落。
zip 是什么?不是拉链,是并行聚合
zip 不是串行等一个完再跑下一个,而是让多个协程并发执行,等全部返回后,把结果按顺序打包成 Pair 或 Triple 一次性交给你。就像叫了三辆网约车,司机各自出发,你坐在家里等他们仨都到门口,再一起上车出发。
实际代码长这样
假设你有三个 suspend 函数:
suspend fun fetchUserInfo(): User { /* 网络调用 */ }
suspend fun fetchOrders(): List<Order> { /* 网络调用 */ }
suspend fun fetchUnreadCount(): Int { /* 网络调用 */ }在 ViewModel 里启动协程,用 async 并发发起请求,再用 await() 和 zip 合并:
viewModelScope.launch {
try {
val (user, orders, count) = async { fetchUserInfo() }
.zip(async { fetchOrders() })
.zip(async { fetchUnreadCount() })
.await()
.let { (pair, count) -> (pair.first, pair.second, count) }
// 更新 UI:user、orders、count 全齐了
_uiState.value = HomeUiState(user, orders, count)
} catch (e: Exception) {
_uiState.value = HomeUiState.error(e.message ?: "加载失败")
}
}注意:Kotlin 标准库的 zip 扩展默认只支持两个协程任务。如果要 zip 三个,就得套一层 zip().zip(),然后用 let 解构。更清爽的做法是引入 kotlinx-coroutines-core 1.7+ 的 awaitAll,但那是另一条路——这里聊的是原生 zip 思路,轻量、不加依赖。
别踩坑:zip 不等于 allOf
有人会混淆 zip 和 awaitAll。前者严格按参数顺序配对返回值(第一个协程结果配第二个,再配第三个),后者返回的是 List<T>,你要自己下标取。如果你的三个请求返回类型不同(User、List<Order>、Int),zip 解构起来更直观;如果都是同类型,awaitAll 写法更扁平。
真实场景小提醒
某次上线前压测发现,首页加载慢了 800ms。查下来是三个接口被写成了串行 await:先等用户信息,再等订单,最后等消息数。改成 zip 后,总耗时直接降到最长那个接口的耗时(比如 300ms),体验立马顺滑。不是所有请求都适合 zip——比如第二个接口依赖第一个返回的 token,那就得老老实实 await;但凡能并行的,zip 就是驱动工具箱里那把趁手的梅花起子,拧得快、不打滑。