侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现一个并发网络请求并处理结果的协程方案

2025-12-11 / 0 评论 / 4 阅读

题目

实现一个并发网络请求并处理结果的协程方案

信息

  • 类型:问答
  • 难度:⭐⭐

考点

结构化并发, 异常处理, 挂起函数组合, 协程作用域管理

快速回答

实现安全并发请求的关键点:

  • 使用 coroutineScopeasync 启动子协程
  • 通过 try/catchCoroutineExceptionHandler 处理异常
  • 使用 awaitAll() 等待所有请求完成
  • ViewModellifecycleScope 中启动协程确保生命周期安全
  • 通过 supervisorScope 实现子协程独立失败
## 解析

场景需求

在Android应用中,需要同时发起3个独立网络请求(获取用户数据/商品列表/消息通知),当所有请求成功时合并结果展示,任一请求失败时进行统一错误处理。

核心实现方案

// 在ViewModel中安全启动协程
fun loadAllData() {
    viewModelScope.launch(CoroutineExceptionHandler { _, e ->
        // 统一错误处理
        showError(e)
    }) {
        try {
            // 结构化并发:三个请求同时启动
            val result = coroutineScope {
                val userDeferred = async { userRepo.getUserData() }
                val productDeferred = async { productRepo.getProductList() }
                val notificationDeferred = async { notificationRepo.getNotifications() }

                // 等待所有请求完成
                awaitAll(userDeferred, productDeferred, notificationDeferred)
            }
            // 处理合并结果
            combineResults(result[0], result[1], result[2])
        } catch (e: Exception) {
            // 补充异常捕获(可选)
        }
    }
}

// 挂起函数示例
suspend fun UserRepository.getUserData(): UserData {
    return withContext(Dispatchers.IO) {
        // 模拟网络请求
        delay(1000)
        UserData(...)
    }
}

关键原理说明

  • 结构化并发coroutineScope 创建子作用域,所有子协程必须完成后父协程才能继续执行
  • 异常传播:默认情况下子协程异常会取消整个作用域,使用 supervisorScope 可实现子协程独立失败
  • 并发控制async 启动并发任务,awaitAll() 等待所有结果
  • 线程调度withContext(Dispatchers.IO) 确保阻塞操作在IO线程执行

最佳实践

  • 作用域管理:在ViewModel中使用 viewModelScope,在Activity中使用 lifecycleScope
  • 异常处理分层
    • 使用 CoroutineExceptionHandler 处理未捕获异常
    • 在关键位置添加 try/catch 处理特定异常
  • 取消传播:协程取消时自动取消所有子协程
  • 超时控制:使用 withTimeout(5000) 添加单次请求超时

常见错误

  • 错误1:在 launch 块直接调用 async 但不调用 await()(导致请求未启动)
  • 错误2:使用 GlobalScope 导致生命周期管理失控
  • 错误3:忘记处理 async 返回的 Deferred 异常
  • 错误4:在主线程执行阻塞网络请求导致ANR

扩展知识

  • 结果聚合优化:使用 listOf(deferred1, deferred2).awaitAll()
  • 部分成功处理
    supervisorScope {
        val userReq = async { /* ... */ }
        val productReq = async { /* ... */ }
        // 单独处理每个请求结果
        userReq.await().onSuccess { /*...*/ }
        productReq.await().onFailure { /*...*/ }
    }
  • Flow替代方案:使用 flow { ... }.zipcombine 操作符合并流