如何使用Swift Combine异步处理任务数组 [英] How to process an array of task asynchronously with swift combine

查看:44
本文介绍了如何使用Swift Combine异步处理任务数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个发布者,该发布者进行网络呼叫并返回ID数组.现在,我需要为每个ID拨打另一个网络电话,以获取我的所有数据.我希望最终的发布者拥有最终的对象.

I have a publisher which takes a network call and returns an array of IDs. I now need to call another network call for each ID to get all my data. And I want the final publisher to have the resulting object.

第一个网络结果:

"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并在它们全部完成后执行.还有更多的组合方法吗?

I would like to perform the network requests in parallel, since they are not dependent on each other. I'm not sure if Future is right here, but I'd imagine I would then have code to do a DispatchGroup or OperationQueue and fulfill when they're all done. Is there more of a Combine way of doing this?

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

Doe Combine have a concept of splitting one stream into many parallel streams and joining the streams together?

推荐答案

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

Combine offers extensions around URLSession to handle network requests unless you really need to integrate with OperationQueue based networking, then Future is a fine candidate. You can run multiple Futures and collect them at some point, but I'd really suggest looking at URLSession extensions for Combine.

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 ,即:

Regarding running a batch of requests, it's possible to use Publishers.MergeMany, i.e:

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 ,并在网络请求完成后,逐个接收上一个示例中的每个 User .

In the example above I use collect to collect all results, which postpones emitting the value to the Sink until all of the network requests successfully finished, however you can get rid of the collect and receive each User in the example above one by one as network requests complete.

这篇关于如何使用Swift Combine异步处理任务数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆