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

时间:2020-05-29 16:13:58

标签: swift combine

我正在尝试建立一个发布者,该发布者将发布一组整数,有时可能会失败。稍作设计,但希望能说明原理。下面的示例。

print

然后我使用以下方式订阅:

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)

    }
}

此方法的工作原理是将错误替换为-1,但是我想继续接收值。有人知道这是否可能吗? 阅读并环顾四周似乎可以使用flatMap,但是我无法确定要在闭包中使用哪个发布者?任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:0)

我认为您错误地认为PassthroughSubject在发布故障后可以发布更多输出。这不可以。呼叫p.send(completion: ...)之后,对p.send(...)的所有呼叫都将被忽略。此外,如果您在致电p之后订阅p.send(completion: ...),则p将立即完成新的订阅,并且不会发送任何输出。

因此,如果您想以后发送更多值,则不能以.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 $0 {
        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($0)
    })

n.start(max: 5)

输出:

1
2
-1
4
5
Finished