Hi there,
I encountered a use case lately where I was iterating over an async sequence and I wanted to fallback to another sequence in case of a failure.
Kotlin Flow can do that:
val base = flowOf { ... }
val other = flowOf { ... }
val flow = flowOf(base).catch { emitAll(other) }
We could have an AsyncSwitchOnErrorSequence
+ an extension on AsyncSequence to create that AsyncSwitchOnErrorSequence
.
The implementation would be something like that:
extension AsyncSequence {
public func switchOnError<Other: AsyncSequence>(
_ other: Other
) -> AsyncSwitchOnErrorSequence<Self, Other> {
AsyncSwitchOnErrorSequence(base: self, other: other)
}
}
public struct AsyncSwitchOnErrorSequence<Base: AsyncSequence, Other: AsyncSequence>: AsyncSequence
where Base.Element == Other.Element {
public typealias AsyncIterator = Iterator
public typealias Element = Base.Element
private let base: Base
private let other: Other
init(base: Base, other: Other) {
self.base = base
self.other = other
}
public func makeAsyncIterator() -> AsyncIterator {
Iterator(
baseIterator: base.makeAsyncIterator(),
otherIterator: other.makeAsyncIterator()
)
}
public struct Iterator: AsyncIteratorProtocol {
var baseIterator: Base.AsyncIterator
var otherIterator: Other.AsyncIterator
var baseHasFailed = false
public mutating func next() async throws -> Element? {
guard !Task.isCancelled else { return nil }
guard !baseHasFailed else {
return try await otherIterator.next()
}
let element: Element?
do {
element = try await baseIterator.next()
} catch {
baseHasFailed = true
element = try await otherIterator.next()
}
return element
}
}
}
extension AsyncSwitchOnErrorSequence: Sendable where Base: Sendable, Other: Sendable { }
Would this be interesting ?