Hello, this was just a small idea to be able to do the following:
func testing() async {
let task = Task {
[0,1,2,3,4].publisher.values // contrived example to show the for await below
}
for await item in task {
print(item)
}
}
versus this:
func testing() async {
let task = Task {
[0,1,2,3,4].publisher.values // contrived example to show the for await below
}
for await item in await task.value {
print(item)
}
}
My implementation:
extension Task: @retroactive AsyncSequence where Success: AsyncSequence, Failure == Success.Failure {
public struct AsyncIterator: AsyncIteratorProtocol {
public typealias Element = Success.Element
public typealias Failure = Success.Failure
private enum State {
case task(Task<Success, Success.Failure>)
case iterator(Success.AsyncIterator)
}
private var state: State
fileprivate init(task: Task<Success, Success.Failure>) {
state = .task(task)
}
public mutating func next() async throws -> Success.Element? {
try await next(isolation: #isolation)
}
mutating public func next(isolation actor: isolated (any Actor)? = #isolation) async throws(Success.Failure) -> Success.Element? {
var iterator = switch state {
case .task(let task): try await task.result.get().makeAsyncIterator()
case .iterator(let iterator): iterator
}
let element = try await iterator.next(isolation: `actor`)
state = .iterator(iterator)
return element
}
}
public func makeAsyncIterator() -> AsyncIterator {
AsyncIterator(task: self)
}
}
What this allows for is a more simple handling of APIs that can easily end up like this:
final actor MySequenceProvider {
func mySequence() async -> AsyncStream<Int> {
return /*...*/
}
}
Which then need an await
to get the sequence. This seems ok, until you account for the many APIs that are still synchronous, and expect to be able to map async sequences without awaiting. So doing this should allow usage of all the AsyncSequence operator methods, on any Task returning an AsyncSequence
.
Now, my implementation is questionable, as I am assuming the Task.Failure
is/should be the same as the returned sequence's Failure
. Which is totally possible that this is wrong, but that was the only way I could figure out how to make the AsyncIterator of the Task use typed throws, without wrapping each possible error in a new type of error.
This honestly feels less like a pitch, and more like a "am I barking up the wrong tree?" type of a prompt.
Thanks!