网络学堂
霓虹主题四 · 更硬核的阅读氛围

Kotlin协程中使用zip合并多个网络请求

发布时间:2026-01-22 18:21:19 阅读:228 次

在开发Android应用时,经常会遇到需要同时请求多个接口并把结果合并处理的场景。比如一个用户主页,既要加载用户基本信息,又要获取最新的动态列表,只有等两个请求都完成才能刷新界面。传统写法容易嵌套回调,代码难看又难维护。

Kotlin协程配合Retrofit,能用很简洁的方式解决这个问题。其中,asyncawaitAll 是常用手段,但如果你需要把多个独立请求的结果组合成一个新的数据结构,zip 操作就显得特别实用。

什么是 zip?

这里的 zip 并不是文件压缩,而是来自函数式编程的概念:把多个异步任务的结果按顺序打包在一起。就像拉链一样,把两边的齿一一对应合上。

假设我们有两个 suspend 函数:

suspend fun fetchUserInfo(): UserInfo {
    delay(1000)
    return UserInfo(name = "张三", age = 28)
}

suspend fun fetchUserPosts(): List<Post> {
    delay(1500)
    return listOf(Post(title = "今天天气不错"), Post(title = "周末去爬山了"))
}

我们想等这两个函数都执行完,然后把用户信息和帖子一起展示。这时候就可以在协程作用域里使用 async 启动两个并发任务,再通过类似 zip 的方式组合结果。

手动实现 zip 效果

Kotlin 标准库没有直接叫 zip 的协程函数,但我们可以通过 async 实现:

val result = coroutineScope {
    val userInfoDeferred = async { fetchUserInfo() }
    val postsDeferred = async { fetchUserPosts() }

    // 等待两个结果,并合并
    UserDetail(
        info = userInfoDeferred.await(),
        posts = postsDeferred.await()
    )
}

这段代码会并发执行两个请求,总耗时取决于最慢的那个(约1.5秒),而不是累加。相比串行请求节省了时间。

封装成通用 zip 辅助函数

如果这类合并操作很多,可以封装一个简单的 zip 函数:

suspend fun <T1, T2> zip(
    call1: suspend () -> T1,
    call2: suspend () -> T2
): Pair<T1, T2> = coroutineScope {
    val deferred1 = async(call1)
    val deferred2 = async(call2)
    deferred1.await() to deferred2.await()
}

调用时就很清晰:

val (userInfo, posts) = zip(::fetchUserInfo, ::fetchUserPosts)
val userDetail = UserDetail(info = userInfo, posts = posts)

这种写法逻辑清楚,也方便后续扩展成支持三个或更多参数的版本。

实际网络请求示例

结合 Retrofit 的 suspend 函数,真实项目中可能是这样的:

interface ApiService {
    @GET("/user/profile")
    suspend fun getUserProfile(): UserProfile

    @GET("/user/feed")
    suspend fun getUserFeed(): FeedResponse
}

// 使用
val api = retrofit.create(ApiService::class.java)

val (profile, feed) = zip(
    { api.getUserProfile() },
    { api.getUserFeed() }
)

页面初始化时发起这两个请求,数据齐了就更新UI,整个过程流畅且无嵌套。

注意:虽然 zip 能合并结果,但如果其中一个请求失败,整个表达式会抛出异常。可以在 await 前加 try-catch 做更精细的错误控制,或者根据业务决定是否要继续等待另一个结果。