出错后如何继续订阅发布者?

How to continue subscribing to publisher after error?

我正在尝试设置一个将发布一组整数的发布者,但有时可能会失败。它有点做作,但希望能说明原理。示例如下。

enum NumberError: Int, Error {
   case isFatal, canContinue
}

struct Numbers {
    let p = PassthroughSubject<Int, NumberError>()

    func start(max: Int) {

        let errorI = Int.random(in: 1...max)
        for i in (1...max) {
            if errorI == i {
                p.send(completion: .failure(NumberError.canContinue))
            } else {
                p.send(i)
            }
        }
        p.send(completion: .finished)

    }
}

然后我订阅使用:

let n = Numbers()
let c = n.p
    .catch {_ in return Just(-1)}

    .sink(receiveCompletion: {result in
        switch result {
        case .failure:
            print("Error")
        case .finished:
            print("Finished")
        }
    }, receiveValue: {
        print([=11=])
    })

n.start(max: 5)

这是因为它用 -1 替换了错误,但我想继续接收值。有谁知道这是否可能? 阅读并环顾四周后,似乎 flatMap 可能是可行的方法,但我无法确定在闭包中使用哪个发布者?非常感谢任何帮助。

我认为您错误地认为 PassthroughSubject 可以在发布失败后发布更多输出。这不可以。在您调用 p.send(completion: ...) 之后,任何对 p.send(...) 的调用都将被忽略。此外,如果您在调用 p.send(completion: ...) 后订阅 pp 将立即完成新订阅并且不会发送任何输出。

因此,如果您想在之后发送更多值,则不能将错误作为 .failure 发送。相反,将发布商的 Output 类型更改为 Result<Int, NumberError> 并将其 Failure 类型更改为 Never:

import Combine

enum NumberError: Int, Error {
   case isFatal, canContinue
}

struct Numbers {
    let p = PassthroughSubject<Result<Int, NumberError>, Never>()

    func start(max: Int) {
        let bad = (max + 1) / 2
        for i in (1...max) {
            if bad == i {
                p.send(.failure(NumberError.canContinue))
            } else {
                p.send(.success(i))
            }
        }
        p.send(completion: .finished)
    }
}

但现在您不能使用 catch 来处理错误,因为它不会作为失败出现。相反,您可以使用 map:

let n = Numbers()
let c = n.p
    .map({
        switch [=11=] {
        case .success(let i): return i
        case .failure(_): return -1
        }
    })
    .sink(receiveCompletion: {result in
        switch result {
        case .failure:
            print("Error")
        case .finished:
            print("Finished")
        }
    }, receiveValue: {
        print([=11=])
    })

n.start(max: 5)

输出:

1
2
-1
4
5
Finished