【问题标题】:How to process an array of task asynchronously with swift combine如何使用 swift combine 异步处理一系列任务
【发布时间】:2020-02-28 16:25:42
【问题描述】:

我有一个发布者,它接受网络调用并返回一个 ID 数组。我现在需要为每个 ID 调用另一个网络调用来获取我的所有数据。我希望最终发布者拥有结果对象。

第一个网络结果:

"user": {
   "id": 0,
   "items": [1, 2, 3, 4, 5]
}

最终对象:

struct User {
    let id: Int
    let items: [Item]
    ... other fields ...
}
struct Item {
    let id: Int
    ... other fields ...
}

处理多个网络调用:

userPublisher.flatMap { user in
    let itemIDs = user.items
    return Future<[Item], Never>() { fulfill in
        ... OperationQueue of network requests ...
    }
}

我想并行执行网络请求,因为它们不相互依赖。我不确定 Future 是否就在这里,但我想我会有代码来做一个 DispatchGroup 或 OperationQueue 并在它们都完成后完成。有没有更多的组合方式来做到这一点?

Combine 是否有将一个流拆分为多个并行流并将这些流连接在一起的概念?

【问题讨论】:

    标签: swift combine


    【解决方案1】:

    Combine 提供围绕URLSession 的扩展来处理网络请求,除非您确实需要与基于OperationQueue 的网络集成,那么Future 是一个不错的选择。您可以运行多个Futures 并在某个时候收集它们,但我真的建议查看Combine 的URLSession 扩展。

    struct User: Codable {
       var username: String
    }
    
    let requestURL = URL(string: "https://example.com/")!
    let publisher = URLSession.shared.dataTaskPublisher(for: requestURL)
        .map { $0.data }
        .decode(type: User.self, decoder: JSONDecoder())
    

    关于运行一批请求,可以使用Publishers.MergeMany,即:

    struct User: Codable {
       var username: String
    }
    
    let userIds = [1, 2, 3]
    
    let subscriber = Just(userIds)
        .setFailureType(to: Error.self)
        .flatMap { (values) -> Publishers.MergeMany<AnyPublisher<User, Error>> in
        let tasks = values.map { (userId) -> AnyPublisher<User, Error> in
                let requestURL = URL(string: "https://jsonplaceholder.typicode.com/users/\(userId)")!
    
                return URLSession.shared.dataTaskPublisher(for: requestURL)
                    .map { $0.data }
                    .decode(type: User.self, decoder: JSONDecoder())
                    .eraseToAnyPublisher()
        }
        return Publishers.MergeMany(tasks)
    }.collect().sink(receiveCompletion: { (completion) in
        if case .failure(let error) = completion {
            print("Got error: \(error.localizedDescription)")
        }
    }) { (allUsers) in
        print("Got users:")
        allUsers.map { print("\($0)") }
    }
    

    在上面的示例中,我使用collect 收集所有结果,这会推迟将值发送到Sink,直到所有网络请求成功完成,但是您可以摆脱collect 并接收每个@上例中的987654333@随着网络请求一一完成。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-08
    • 2018-04-10
    • 2015-07-18
    • 2019-08-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多