【问题标题】:How to make Combine.Zip work with failure and success of two `Publishers`如何使 Combine.Zip 在两个“发布者”的失败和成功的情况下工作
【发布时间】:2022-08-24 15:52:34
【问题描述】:

我试图让Zip 两个Publishers 具有两个不同的功能,但它没有按预期工作。我有机会一次失败,一次成功。但即使是成功的,我也没有在sink 中得到成功的回复。这是我的代码,非常感谢帮助。


struct ContentView: View {
    @State var buttonTapped = false
    @State var cancellable = Set<AnyCancellable>()

    var body: some View {
        Group {
           Text(\"Home\")
        }.onAppear {
            Publishers.Zip(fetchData1(), fetchData2())
                .sink(receiveCompletion: { first in
                switch first {
                case .failure(let er):
                    print(er)
                case .finished:
                    print(\"ss\")
                }
            }, receiveValue: { (a, b) in
               print(a, b)
             // Not printing success value
            }).store(in: &cancellable)
        }
    }

    func fetchData1() -> Future<Bool, ErrorType> {
        return Future { promise in
            promise(.failure(.error(\"fetchData1 failed\")))
        }
    }
    
    func fetchData2() -> Future<String, ErrorType> {
        return Future { promise in
            promise(.success(\"fetchData success \"))
        }
    }
}

enum ErrorType: Error {
    case error(String)
}


  • 你真正想要达到的目标是什么? Zip 仅在以下情况下发出值 N两个都第 N 次发出的值。因此,如果它的两个上游都完成了 N 个元素,则一个完成,然后 Zip 本身也完成了,而无需等待更多元素。这意味着如果您Zip 2 Futures,其中一个失败,Zip 不能发出任何元素,因为它的上游之一失败而没有发出任何值。 Zip 不是您要寻找的运算符,但是,如果不知道您要解决的实际问题是什么,则很难说出您真正需要哪个运算符。
  • @Dávid Pásztor 感谢您的评论,如果我知道我为什么要在这里发帖?告诉我,如果你能,我该如何处理我的案子。我刚开始使用combine。
  • 这就是问题所在,你没有解释什么是你的情况.您刚刚表明某些事情没有按预期工作。请解释您要解决的实际问题。您正在尝试合并 2 个Publishers,但是如果其中任何一个失败,应该是什么行为?你还想要对方的价值吗?你想在什么时候收到这个值,只要他们中的任何一个发出它,还是只有在两者都发出后?
  • 我有两个不同的要求,Case: 1 如果第一个请求失败,请不要继续下一个请求。 Case: 2 甚至 1 都无法继续到第二个请求句柄,以防两者都有错误。我在这两者之间感到困惑。

标签: ios swift combine


【解决方案1】:

下面是一个演示每个案例的游乐场。

它包括一个函数makeRequest,它创建一个Future 来模拟成功或失败的请求。

样本的核心是一个模式:

someFailableRequest
  .tryMap { return a value indicating success or failure }
  .catch { _ in Just(a value indicating success or failure) }

如果请求成功,我们传递给后面阶段的值表示成功。如果失败,则catch 返回一个发布者,该发布者生成相同类型的东西,但表示失败。

该模式用于递归地以稍微不同的方式处理每种情况。

import Cocoa
import Combine
import Foundation

func makeRequest(shouldFail: Bool) -> AnyPublisher<String, Error> {
    return Future<String, Error> { fulfill in
        let delay = (100...500).randomElement()!
        DispatchQueue.global(qos: .background).asyncAfter(
            deadline: .now() + DispatchTimeInterval.milliseconds(delay)) {
                if shouldFail {
                    fulfill(.failure(NSError(domain: "sadness", code: -1)))
                } else {
                    fulfill(.success("Yea!"))
                }
            }
    }.eraseToAnyPublisher()
}

// Case 1.  First request fails, don't do second one
let request1 = makeRequest(shouldFail: true)
let request2 = makeRequest(shouldFail: false)

let requestChain = request1
    .tryMap { return $0 as String? }
    .catch { _ in return Just(nil as String?) }
    .flatMap { (firstResult: String?) -> AnyPublisher<(String?, String?), Never> in
        if firstResult != nil {
            return request2
                .tryMap { result2 in (firstResult, result2) }
                .catch { _ in Just((firstResult, nil)) }
                .eraseToAnyPublisher()
        } else {
            return Just((nil as String?, nil as String?)).eraseToAnyPublisher()
        }
    }

let cancellable = requestChain.sink { debugPrint("first chain \($0)") }

// Case2 : continue second request even if first fails.

let request3 = makeRequest(shouldFail: true)
let request4 = makeRequest(shouldFail: false)

let requestChain1 =
    request3
    .tryMap { firstResult -> AnyPublisher<(String?,String?),Never> in
        request4
            .tryMap { secondResult in (firstResult, secondResult) }
            .catch { _ in Just( (firstResult as String?, nil) ).eraseToAnyPublisher() }
            .eraseToAnyPublisher()
    }
    .catch { _ in
        Just(request4
            .tryMap { secondResult in (nil as String?, secondResult) }
            .catch { _ in Just( (nil as String?, nil as String?) ) }
            .eraseToAnyPublisher())
    }
    .flatMap { $0 }


let cancellable2 = requestChain1.sink { debugPrint("second chain \($0)") }

【讨论】:

    【解决方案2】:

    你的代码很好。不确定你的要求,但它是这样工作的,

    • 其中任何一个失败,全部失败
    • 只有两个成功,它成功了
    • 我猜如果另一个数据失败,我们将无法获得一个数据

    查看doc from apple: 使用 Publishers.Zip 组合来自两个发布者的最新元素并向下游发出一个元组。返回的发布者等到两个发布者都发出了一个事件,然后将来自每个发布者的最旧的未使用事件作为一个元组一起传递给订阅者。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-07
      • 2016-04-18
      相关资源
      最近更新 更多