仅在 Swift 中执行多个并发(async let)任务并在所有任务都失败时抛出错误

问题描述 投票:0回答:1

例如,我需要从不同来源获取两个相同类型的数组。这两个请求都可能会失败,但是当这些请求同时失败时,我需要显示任何错误(由服务器给出)。

这是一个常见的示例,但当任何请求失败时它都会抛出错误:

    func test() async throws -> [Any] {
        async let t1 = getArr()
        async let t2 = getArr2()
        return try await t1 + t2
    }
    
    func getArr() async throws -> [Any] {
        []
    }
    
    func getArr2() async throws -> [Any] {
        []
    }

同时,如果我使用

try?
,我会丢失请求返回的错误。

我也可以删除

throws
并用
Result<[Any], Error>
替换返回结果,但看起来很奇怪。

如何正确解决这个问题?

swift async-await concurrency task throws
1个回答
0
投票

你绝对应该在这里使用

Result
。为了方便起见,首先编写一个
Result
初始化程序来捕获异步抛出闭包中的错误。

extension Result {
    init(asyncCatching block: () async throws -> Success) async where Failure == Error {
        do {
            self = .success(try await block())
        } catch {
            self = .failure(error)
        }
    }
}

假设您希望

test
在所有
async let
抛出时抛出所有错误,请将
[Error]
也设为
Error
类型。

extension Array: Error where Element: Error {}

那么就很简单:

func test() async throws -> [Any] {
    async let t1 = Result { try await getArr() }
    async let t2 = Result { try await getArr2() }
    switch (await t1, await t2) {
    case let (.failure(e1), .failure(e2)):
        return [e1, e2]
    case let (.success(s), .failure), let (.failure, .success(s)):
        return s
    case let (.success(s1), .success(s2)):
        return s1 + s2
    }
}

对于“将

Result
的集合减少为单个值或抛出”操作的更通用版本,您可以编写:

func accumulateResults<T, U, Failure: Error>(_ results: [Result<T, Failure>], identity: U, reduce: (U, T) -> U) throws -> U {
    var ret = identity
    var success = false
    var failures = [Failure]()
    for result in results {
        switch result {
        case .success(let s):
            ret = reduce(ret, s)
        case .failure(let e):
            if success { break }
            failures.append(e)
        }
    }
    return if success { ret } else { throw failures }
}

如果不使用

Result
(或重新发明类似的东西),
test
只能抛出最后一个错误,因为你无法存储任何以前的错误(
Result
的确切工作)。

func testWithoutResult() async throws -> [Any] {
    async let t1 = getArr()
    async let t2 = getArr2()
    var result = [Any]()
    var success = false
    do {
        result.append(try await t1)
        success = true
    } catch {}
    do {
        result.append(try await t2)
    } catch {
        if !success { throw error }
    }
    return result
}

对于每个非最终异步操作,编写

do
块并设置
success = true
。对于最终的异步操作,请改为在 catch 块中
if !success { throw error }

© www.soinside.com 2019 - 2024. All rights reserved.