Swift异步抛出任务组:部分任务抛出时能否收集成功结果?
Swift:忽略异步任务错误并收集结果的优化写法
问题背景
处理整数数组时,每个元素的异步处理函数可能抛出错误或返回结果。使用withThrowingTaskGroup时,若用类似map()的方式会因任一任务报错导致无法获取任何结果。希望实现两个目标:
- 忽略报错任务,仅收集成功执行的结果
- 扩展需求:获取包含所有元素对应成功值或错误的集合(格式如
[1: Error(), 2: 20, 3: Error()...])
原实现代码:
func asyncThrowOnOddNumbers(_ num: Int) async throws -> (Int, Int) { if num % 2 == 1 { throw NSError(domain: "OK", code: 1) } return (num, num * 10) } let numSet = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] let results = await withThrowingTaskGroup(of: (Int, Int).self) { group in for num in numSet { group.addTask { return try await asyncThrowOnOddNumbers(num) } } var successDir: [Int : Int] = [:] while let workResult = await group.nextResult() { switch workResult { case .success(let value): successDir[value.0] = value.1 case .failure(_): continue } } return successDir } // 目标输出: [2: 20, 6: 60, 10: 100, 8: 80, 4: 40] // 扩展需求: [1: Error(), 2: 20, 3: Error(), 4: 40, 5: Error()...]
一、仅收集成功结果的简洁写法
利用reduce(into:)替代手动循环和switch判断,代码更紧凑且逻辑清晰:
let successResults = await withThrowingTaskGroup(of: (Int, Int).self) { group in // 简化任务添加逻辑 numSet.forEach { group.addTask { try await asyncThrowOnOddNumbers($0) } } // 用reduce直接收集成功结果 return await group.reduce(into: [Int: Int]()) { dict, taskResult in guard let (key, value) = try? taskResult.get() else { return } dict[key] = value } }
优化点:
- 用
forEach替代for循环,简化任务添加代码 - 借助
reduce(into:)和try?过滤错误,直接构建结果字典,省去冗余的while循环和switch分支
二、扩展需求:收集所有元素的成功/错误结果
要关联每个原始数字与其对应的结果(成功或错误),需让任务返回包含原始数字的Result类型,再统一收集:
方式1:构建字典(键为原始数字)
let fullResultDict = await withThrowingTaskGroup(of: (Int, Result<Int, Error>).self) { group in numSet.forEach { num in group.addTask { do { let (_, value) = try await asyncThrowOnOddNumbers(num) return (num, .success(value)) } catch { return (num, .failure(error)) } } } return await group.reduce(into: [Int: Any]()) { dict, item in let (num, result) = item dict[num] = result } }
方式2:保持原数组顺序的结果数组
如果需要结果顺序与原数组一致,可通过索引关联后排序:
let orderedFullResults = await withThrowingTaskGroup(of: (index: Int, result: Result<Int, Error>).self) { group in for (index, num) in numSet.enumerated() { group.addTask { do { let (_, value) = try await asyncThrowOnOddNumbers(num) return (index, .success(value)) } catch { return (index, .failure(error)) } } } // 收集结果并按原索引排序 let unsortedResults = await group.reduce(into: [(Int, Result<Int, Error>)]()) { array, item in array.append((numSet[item.index], item.result)) } return unsortedResults.sorted { $0.0 < $1.0 } }
内容的提问来源于stack exchange,提问作者CalebK
相关产品推荐
相关产品推荐

