You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift异步抛出任务组:部分任务抛出时能否收集成功结果?

Swift:忽略异步任务错误并收集结果的优化写法

问题背景

处理整数数组时,每个元素的异步处理函数可能抛出错误或返回结果。使用withThrowingTaskGroup时,若用类似map()的方式会因任一任务报错导致无法获取任何结果。希望实现两个目标:

  1. 忽略报错任务,仅收集成功执行的结果
  2. 扩展需求:获取包含所有元素对应成功值或错误的集合(格式如[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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 19:40:09