为什么我们在 Combine 中没有 tryFlatMap 运算符?
Why don't we have tryFlatMap operator in Combine?
我们应该用什么来代替?
我很惊讶之前没有人问过这个问题。
严格来说您不需要 tryFlatMap
,因为 flatMap
的转换 return 是一个发布者。如果发现错误,您可以在转换闭包中使用 do/catch
和 return 一个 Fail
发布者。
import Combine
func someFunction(of i: Int) throws -> AnyPublisher<Int, Error> {
return Just(i + 1)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
}
let upstream: AnyPublisher<Int, Error> = Just(100)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
upstream
.flatMap({ i -> AnyPublisher<Int, Error> in
do {
return try someFunction(of: i).eraseToAnyPublisher()
} catch {
return Fail(error: error).eraseToAnyPublisher()
}
})
如果愿意,您可以编写自己的 tryFlatMap
运算符:
extension Publisher {
func tryFlatMap<Pub: Publisher>(
maxPublishers: Subscribers.Demand = .unlimited,
_ transform: @escaping (Output) throws -> Pub
) -> Publishers.FlatMap<AnyPublisher<Pub.Output, Error>, Self> {
return flatMap(maxPublishers: maxPublishers, { input -> AnyPublisher<Pub.Output, Error> in
do {
return try transform(input)
.mapError { [=11=] as Error }
.eraseToAnyPublisher()
} catch {
return Fail(outputType: Pub.Output.self, failure: error)
.eraseToAnyPublisher()
}
})
}
}
然后像这样使用它:
upstream
.tryFlatMap { try someFunction(of: [=12=]) }
还有一个解决方案。
tryFlatMap = tryMap + flatMap。
例如
整个故事开始于我只是想打开一个可选的包装。如果它是零,我希望它只是失败。
let upstream = Just(siteURL)
.tryMap { url -> URL in
guard let url = url else { throw Errors.invalidSiteURL }
return url
}
.flatMap {
URLSession.shared.dataTaskPublisher(for: [=10=]).mapError { [=10=] as Error }
}
[编辑]像这样也有效:
let container = Just(siteURL)
.tryMap { url -> URLSession.DataTaskPublisher in
guard let url = url else { throw Errors.invalidSiteURL }
return URLSession.shared.dataTaskPublisher(for: url)
}
.map { [=11=].mapError { [=11=] as Error } }
.switchToLatest()
乱七八糟的错误类型有点烦人。
我们应该用什么来代替?
我很惊讶之前没有人问过这个问题。
严格来说您不需要 tryFlatMap
,因为 flatMap
的转换 return 是一个发布者。如果发现错误,您可以在转换闭包中使用 do/catch
和 return 一个 Fail
发布者。
import Combine
func someFunction(of i: Int) throws -> AnyPublisher<Int, Error> {
return Just(i + 1)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
}
let upstream: AnyPublisher<Int, Error> = Just(100)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
upstream
.flatMap({ i -> AnyPublisher<Int, Error> in
do {
return try someFunction(of: i).eraseToAnyPublisher()
} catch {
return Fail(error: error).eraseToAnyPublisher()
}
})
如果愿意,您可以编写自己的 tryFlatMap
运算符:
extension Publisher {
func tryFlatMap<Pub: Publisher>(
maxPublishers: Subscribers.Demand = .unlimited,
_ transform: @escaping (Output) throws -> Pub
) -> Publishers.FlatMap<AnyPublisher<Pub.Output, Error>, Self> {
return flatMap(maxPublishers: maxPublishers, { input -> AnyPublisher<Pub.Output, Error> in
do {
return try transform(input)
.mapError { [=11=] as Error }
.eraseToAnyPublisher()
} catch {
return Fail(outputType: Pub.Output.self, failure: error)
.eraseToAnyPublisher()
}
})
}
}
然后像这样使用它:
upstream
.tryFlatMap { try someFunction(of: [=12=]) }
还有一个解决方案。
tryFlatMap = tryMap + flatMap。
例如
整个故事开始于我只是想打开一个可选的包装。如果它是零,我希望它只是失败。
let upstream = Just(siteURL)
.tryMap { url -> URL in
guard let url = url else { throw Errors.invalidSiteURL }
return url
}
.flatMap {
URLSession.shared.dataTaskPublisher(for: [=10=]).mapError { [=10=] as Error }
}
[编辑]像这样也有效:
let container = Just(siteURL)
.tryMap { url -> URLSession.DataTaskPublisher in
guard let url = url else { throw Errors.invalidSiteURL }
return URLSession.shared.dataTaskPublisher(for: url)
}
.map { [=11=].mapError { [=11=] as Error } }
.switchToLatest()
乱七八糟的错误类型有点烦人。