如何使用Swift Combine异步处理任务数组 [英] How to process an array of task asynchronously with 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 Future
s 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屋!