Kotlin:如何在foreach循环中等待所有异步任务完成
解决方案
问题出在你直接调用launch启动协程后,没有等待这些协程全部完成就执行了后续代码。Kotlin协程提供了优雅的非阻塞方式等待所有子任务完成,下面是两种可行方案:
方案一:使用coroutineScope等待所有子协程
coroutineScope会自动挂起当前协程,直到内部所有子协程全部完成后才继续执行后续代码,完美适配你的需求:
val failedTasks = mutableListOf<ApiTask<T>>() App.log("apiCallChained: startCall") // 用coroutineScope包裹所有任务,等待全部完成 coroutineScope { apiBlocks.forEachIndexed { i, apiBlock -> App.log("apiCallChained: callingTask: $i") launch(Dispatchers.Main) { val (r, err) = withContext(Dispatchers.IO) { try { apiBlock.apiBlock(this) to null } catch (e: ApiCallError) { null to e } } when { r != null -> { App.log("apiCallChained: callingTask: $i - success") apiBlock.onSuccess(r) } err != null -> { App.log("apiCallChained: callingTask: $i - error") failedTasks.add(apiBlock) } } } } } // 所有任务完成后才会执行到这里 App.log("apiCallChained: endCall: ${failedTasks.size}") if (failedTasks.isEmpty()) onSuccess.invoke() else onError.invoke(failedTasks)
方案二:使用async+awaitAll收集任务结果
如果需要更灵活地处理每个任务的返回值,可以用async创建Deferred对象,再用awaitAll等待全部完成:
val failedTasks = mutableListOf<ApiTask<T>>() App.log("apiCallChained: startCall") coroutineScope { // 收集所有异步任务 val tasks = apiBlocks.mapIndexed { i, apiBlock -> async(Dispatchers.Main) { App.log("apiCallChained: callingTask: $i") val (r, err) = withContext(Dispatchers.IO) { try { apiBlock.apiBlock(this) to null } catch (e: ApiCallError) { null to e } } // 返回索引和结果对,方便后续处理 Pair(i, apiBlock to err) } } // 等待所有任务完成,统一处理结果 tasks.awaitAll().forEach { (i, pair) -> val (apiBlock, err) = pair if (err == null) { App.log("apiCallChained: callingTask: $i - success") // 这里可以提前在async中保存成功结果,避免重复调用apiBlock apiBlock.onSuccess(apiBlock.apiBlock(this)) } else { App.log("apiCallChained: callingTask: $i - error") failedTasks.add(apiBlock) } } } App.log("apiCallChained: endCall: ${failedTasks.size}") if (failedTasks.isEmpty()) onSuccess.invoke() else onError.invoke(failedTasks)
关键说明
- 两种方案都依赖
coroutineScope,它不会阻塞线程,而是挂起当前协程,直到所有子协程完成,是Kotlin协程中等待批量任务的标准方式。 - 因为你在
launch(Dispatchers.Main)中修改failedTasks,所有修改操作都在主线程执行,不存在线程安全问题;如果后续切换到其他线程修改,建议使用线程安全集合(如ConcurrentLinkedQueue)。
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

