gpt4 book ai didi

swift - 如何使用Combine + Swift复制PromiseKit风格的链式异步流

转载 作者:行者123 更新时间:2023-12-03 09:19:04 28 4
gpt4 key购买 nike

我在一个项目中成功地使用了 PromiseKit,直到 Xcode 11 beta 破坏了 PK v7。为了减少外部依赖,我决定废弃 PromiseKit。处理链式异步代码的最佳替代品似乎是使用新组合框架的 Futures。
我正在努力使用 Combine 复制简单的 PK 语法
前任。简单的 PromiseKit 链式异步调用语法

getAccessCodeFromSyncProvider.then{accessCode in startSync(accessCode)}.then{popToRootViewController}.catch{handleError(error)}

I understand:

A Swift standard library implementation of async/await would solve this problem (async/await does not yet exist, despite lots of chatter and involvement from Chris Latter himself)



I could replicate using Semaphores (error-prone?)



flatMap can be used to chain Futures



我想要的异步代码应该能够按需调用,因为它涉及确保用户登录。我正在努力解决两个概念性问题。
  • 如果我将 Futures 包装在一个方法中,使用 sink为了处理结果,似乎在 sink 调用订阅者之前该方法超出了范围。 .
  • 由于 Futures 只执行一次,我担心如果我多次调用该方法,我只会从第一次调用中得到旧的、陈旧的结果。要解决这个问题,也许我会使用 PassthroughSubject?这允许按需调用发布者。

  • 问题:
  • 我是否必须保留每个发布者和订阅者之外的
    调用方法
  • 如何使用 Swift 标准库复制简单的链式异步,然后将其嵌入到我可以按需调用以从顶部重新启动链式异步调用的 swift 实例方法中?

  • //how is this done using Combine?
    func startSync() {
    getAccessCodeFromSyncProvider.then{accessCode in startSync(accessCode)}.catch{\\handle error here}
    }

    最佳答案

    这不是你整个问题的真正答案——只是关于如何开始使用 Combine 的部分。我将演示如何使用Combine 框架链接两个异步操作:

        print("start")
    Future<Bool,Error> { promise in
    delay(3) {
    promise(.success(true))
    }
    }
    .handleEvents(receiveOutput: {_ in print("finished 1")})
    .flatMap {_ in
    Future<Bool,Error> { promise in
    delay(3) {
    promise(.success(true))
    }
    }
    }
    .handleEvents(receiveOutput: {_ in print("finished 2")})
    .sink(receiveCompletion: {_ in}, receiveValue: {_ in print("done")})
    .store(in:&self.storage) // storage is a persistent Set<AnyCancellable>

    首先,你关于持久化的问题的答案是:最终订阅者必须持久化,这样做的方法是使用 .store方法。通常你会有一个 Set<AnyCancellable>作为属性(property),就像这里一样,您只需调用 .store作为将您的订阅者放入其中的最后一件事。

    接下来,在这个管道中,我正在使用 .handleEvents只是为了在管道移动时给自己一些打印输出。这些只是诊断,在实际实现中不存在。所有 print声明纯粹是为了让我们可以谈论这里发生的事情。

    那么会发生什么呢?
    start
    finished 1 // 3 seconds later
    finished 2 // 3 seconds later
    done

    所以你可以看到我们链接了两个异步操作,每个操作需要 3 秒。

    我们是怎么做的?我们从一个 Future 开始,它必须调用它的传入 promise方法在完成时将 Result 作为完成处理程序。之后,我们使用了 .flatMap产生另一个 Future 并将其投入运行,再次做同样的事情。

    所以结果并不漂亮(就像 PromiseKit),但它是一个异步操作链。

    在Combine 之前,我们可能已经使用某种Operation/OperationQueue 依赖项来完成此操作,这可以正常工作,但与PromiseKit 的直接易读性更低。

    现实一点

    说了这么多,这里有一个更现实的重写:
    var storage = Set<AnyCancellable>()
    func async1(_ promise:@escaping (Result<Bool,Error>) -> Void) {
    delay(3) {
    print("async1")
    promise(.success(true))
    }
    }
    func async2(_ promise:@escaping (Result<Bool,Error>) -> Void) {
    delay(3) {
    print("async2")
    promise(.success(true))
    }
    }
    override func viewDidLoad() {
    print("start")
    Future<Bool,Error> { promise in
    self.async1(promise)
    }
    .flatMap {_ in
    Future<Bool,Error> { promise in
    self.async2(promise)
    }
    }
    .sink(receiveCompletion: {_ in}, receiveValue: {_ in print("done")})
    .store(in:&self.storage) // storage is a persistent Set<AnyCancellable>
    }

    如您所见,我们 future 出版商的想法只需传递 promise打回来;他们实际上不必是调用他们的人。一个 promise因此可以在任何地方调用回调,直到那时我们才会继续。

    因此,您可以很容易地了解如何更换人工 delay一个真正的异步操作,不知何故掌握了这个 promise回调并可以在完成时调用它。此外,我的 promise Result 类型纯粹是人为的,但您可以再次看到它们如何用于在管道中传达有意义的东西。当我说 promise(.success(true)) ,这会导致 true弹出管道的末端;我们在这里忽略了这一点,但它可能是某种彻头彻尾的有用值(value),甚至可能是下一个 Future。

    (还要注意,我们可以在链中的任何点插入 .receive(on: DispatchQueue.main),以确保紧随其后的内容在主线程上启动。)

    稍微整洁一点

    我还想到我们可以通过将我们的 Future 发布者移到常量中来使语法更简洁,也许更接近 PromiseKit 可爱的简单链。但是,如果您这样做,您可能应该将它们包装在 Deferred 发布者中以防止过早评估。例如:
    var storage = Set<AnyCancellable>()
    func async1(_ promise:@escaping (Result<Bool,Error>) -> Void) {
    delay(3) {
    print("async1")
    promise(.success(true))
    }
    }
    func async2(_ promise:@escaping (Result<Bool,Error>) -> Void) {
    delay(3) {
    print("async2")
    promise(.success(true))
    }
    }
    override func viewDidLoad() {
    print("start")
    let f1 = Deferred{Future<Bool,Error> { promise in
    self.async1(promise)
    }}
    let f2 = Deferred{Future<Bool,Error> { promise in
    self.async2(promise)
    }}
    // this is now extremely neat-looking
    f1.flatMap {_ in f2 }
    .receive(on: DispatchQueue.main)
    .sink(receiveCompletion: {_ in}, receiveValue: {_ in print("done")})
    .store(in:&self.storage) // storage is a persistent Set<AnyCancellable>
    }

    关于swift - 如何使用Combine + Swift复制PromiseKit风格的链式异步流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59428026/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com