如何在Combine框架中将URLSession.DataTaskPublisher
转换为Future
。
我认为,Future发布者在这里更合适,因为该调用只能发出一个响应,最终会失败。
RxSwift中有asSingle
之类的帮助程序方法。
我已经通过以下方法实现了这种转换,但是不知道这是否是最好的方法。
return Future<ResponseType, Error>.init { (observer) in
self.urlSession.dataTaskPublisher(for: urlRequest)
.tryMap { (object) -> Data in
//......
}
.receive(on: RunLoop.main)
.sink(receiveCompletion: { (completion) in
if case let .failure(error) = completion {
observer(.failure(error))
}
}) { (response) in
observer(.success(response))
}.store(in: &self.cancellable)
}
}
有没有简单的方法可以做到这一点?
答案 0 :(得分:4)
据我了解,在RxSwift中使用.asSingle
的原因是,当您订阅时,您的订阅者会收到SingleEvent
或{{1} }。因此,您的订阅者不必担心会收到.success(value)
类型的事件,因为没有一个事件。
没有与Combine中的等效。从用户的角度来看,在Combine中,.error(error)
只是另一种.completion
,可以发出输出值和Future
或Publisher
。类型系统不会强制执行.finished
从不发出.failure(error)
的事实。
因此,没有程序上的原因专门返回Future
。您可能会争辩说,返回.finished
证明了您总是总是恰好返回一个输出或失败的意图。但这并不会改变您编写订阅者的方式。
此外,由于Combine大量使用泛型,因此只要您想将任何运算符应用于Future
,就没有前途了。如果将Future
应用于某些Future
,则会得到map
,并且对于其他所有运算符也是如此。这些类型很快就变得一发不可收拾,并掩盖了底部有Future<V, E>
的事实。
如果确实需要,可以实现自己的运算符将任何Map<Future<V, E>, V2>
转换为Future
。但是您必须决定上游发出Publisher
时该怎么做,因为Future
无法发出.finished
。
Future
答案 1 :(得分:2)
不是将数据任务 publisher 转换为Future,而是将 data任务转换为Future。只需将Future包裹在对URLSession的[ERROR] The project com.company.jcompany:persistence-api:5.0.0-SNAPSHOT (/var/lib/jenkins/jobs/jcompany_persistence_api-trunk/workspace/pom.xml) has 1 error
[ERROR] Non-resolvable parent POM: Could not transfer artifact com.company.jcompany:persistence:pom:5-SNAPSHOT from/to company-repo-snap (${company.repo.download.endpoint}/libs-snapshots): No connector available to access repository company-repo-snap (${company.repo.download.endpoint}/libs-snapshots) of type default using the available factories WagonRepositoryConnectorFactory and 'parent.relativePath' points at wrong local POM @ line 5, column 10 -> [Help 2]
org.apache.maven.model.resolution.UnresolvableModelException: Could not transfer artifact com.company.jcompany:persistence:pom:5-SNAPSHOT from/to company-repo-snap (${company.repo.download.endpoint}/libs-snapshots): No connector available to access repository company-repo-snap (${company.repo.download.endpoint}/libs-snapshots) of type default using the available factories WagonRepositoryConnectorFactory
at org.apache.maven.project.ProjectModelResolver.resolveModel(ProjectModelResolver.java:159)
at org.apache.maven.model.building.DefaultModelBuilder.readParentExternally(DefaultModelBuilder.java:813)
at org.apache.maven.model.building.DefaultModelBuilder.readParent(DefaultModelBuilder.java:664)
at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:310)
at org.apache.maven.model.building.DefaultModelBuilder.build(DefaultModelBuilder.java:232)
的调用周围,即可解决问题。这正是未来的发展方向:将任何异步操作转变为发布者。
答案 2 :(得分:0)
您需要将现有发布者转换为 AnyPublisher<Value, Error>
,而不是尝试从函数返回“未来”。您可以使用 .eraseToAnyPublisher()
运算符执行此操作。
func getUser() -> AnyPublisher<User, Error> {
URLSession.shared.dataTaskPublisher(for: request)
.tryMap { output -> Data in
// handle response
return output.data
}
.decode(type: User.self, decoder: JSONDecoder())
.mapError { error in
// handle error
return error
}
.eraseToAnyPublisher()
}