提问人:Gargo 提问时间:6/16/2023 更新时间:6/16/2023 访问量:222
在 Swift Combine 中按顺序将 Publisher 映射到另一个发布者,并单独(或跳过)处理每个步骤的错误?
Map Publisher to another publishers sequentially and handle errors for each step separately (or skip) in Swift Combine?
问:
法典:
import Combine
func login() -> Future<Token, Error> { ... }
func updateImage() -> Future<Status, Never> { ... }
func getProfile() -> Future<Profile, Error> { ... }
我需要执行如下操作(顺序操作):
login()
.catch { error in
... //handle error
}
.flatMap { token in
...//handle login results
return updateImage()
}
.catch { _ in
... //skip error
}
.flatMap {
... //handle updateImage results
return getProfile()
}
.sink(...) //handle getProfile results and errors
问题是内部有误导性的类型和 .Combine
flatMap
catch
尝试返回捕获块内部:Empty
return Empty<String, CustomError>(completeImmediately: true)
.eraseToAnyPublisher()
但我不明白它是否停止在部分中产生错误。对于我的任务来说,这是正确的方法吗?sink
答:
-1赞
Dávid Pásztor
6/16/2023
#1
如果你正在使用 s,并且想要将多个一次性异步调用彼此链接,那么使用方法比使用 Combine 要好得多。Future
async
不幸的是,Combine 没有类型级支持只发出 1x 然后总是完成的 s。 尝试实现此目的,但是一旦将任何运算符应用于 ,就会得到一个 ,因此无法构建一个 Combine 管道来保证其每个步骤只能发出 1 倍。Publisher
Future
Future
Publisher
另一方面,方法实现了这个确切的目标。async
func login() async throws -> Token { ... }
func updateImage() async -> Status { ... }
func getProfile() async throws -> Profile { ... }
do {
let token: Token
do {
token = try await login()
} catch {
token = // assign a default value
}
let status = try await updateImage()
let profile = try await getProfile()
} catch {
// handle profile errors
}
评论
0赞
Sweeper
6/16/2023
虽然我同意异步方法更合适,但我不同意“所以你不能构建一个保证其每个步骤只能发出 1 倍的 Combine 管道”每个 Combine 运算符都记录在案并执行非常具体的事情。为什么不能保证?静态分析一个人使用过的运算符,在某些情况下会有保证。
0赞
Dávid Pásztor
6/16/2023
@Sweeper类型系统中内置的文档和编译时保证是截然不同的东西。再次使用第三方工具进行静态分析,与语言级支持截然不同。
0赞
Sweeper
6/16/2023
#2
如果要链接多个这些独立的 s,并在每个步骤中处理错误,可以遵循以下模式:Future
future().map { result in
// handle the future's result
// this implicitly returns Void, turning it into a publisher of Void
}
.catch { error in
// handle error...
// in the case of an error,
// if you want the pipeline to continue, return Just(())
// if you want the pipeline to stop, return Empty()
}
其中每个都是发布一个值或根本不发布值的发布者。然后,您可以将其中的多个链接在一起:()
flatMap
let cancellable = login().map { token in
// handle login result...
return ()
}
.catch { error in
// handle login error...
return Just(())
}
.flatMap { _ in
updateImage().map { status in
// handle updateImage results...
}
// no need for .catch here because updateImage doesn't fail
}
.flatMap { _ in
getProfile().map { profile in
// handle getProfile results...
}.catch { error in
// handle getProfile errors...
return Just(())
}
}.sink { completion in
// handle completion
} receiveValue: { _ in
// you will only recieve a () here
}
为了帮助编译器更快地找出类型,甚至根本不知道类型,您应该添加显式返回类型和/或在适当的情况下。eraseToAnyPublisher()
正如 Dávid Pásztor 的回答所说,如果 and 等是方法,则这种链接直接内置于语言中。您可以像编写顺序语句一样编写“链”。login
async
func login() async throws -> Token { ... }
func updateImage() async -> Status { ... }
func getProfile() async throws -> Profile { ... }
func asyncChain() async {
do {
let token = try await login()
// handle login results...
} catch {
// handle login error...
}
let status = await updateImage()
// handle updateImage results...
do {
let profile = try await getProfile()
// handle getProfile results...
} catch {
// handle getProfile error...
}
}
评论
0赞
Gargo
6/16/2023
它不适合我,但至少在您的帮助下,我在代码中发现了一个错误 - 在之前,应该返回一个与前面的链步骤中获得的发布者具有相同值类型的发布者。catch
flatMap
catch
评论
map
flatMap
catch
Just