Why have separate `Continuation` object with `AsyncStream`

Seems like it would have been more straightforward to just have all those methods/properties on the stream itself and just keep the fact that the corresponding logic is encapsulated in a Continuation object an implementation detail of AsyncStream.

The idea behind having two separate types here is that the Continuation can be passed to the producer system while the AsyncStream to the consumer system. This allows to observe when either of the two systems drops/deinits their instance of either the continuation or stream and inform the other side about it.

1 Like

so this is just a workaround to prevent having to explicitly do something like call stream.finish() within the producer system?

do you have an example of where a consumer would need to know when the producer finished rather than just allow a for await to just keep going until the consuming instance/context was terminated?

I will try to explain it with a simple example.

class LocationProvider {
  private var continuation: AsyncStream<CLLocation>.Continuation!
  
  let locationStream = AsyncStream<CLLocation> { continuation in
    self.continuation = continuation // compiler error: can not assign continuation
  }

  private func push(location: CLLocation) {
    continuation.yield(location)
  }
}

Instead of this we can do:

class LocationProvider {
  private let continuation: AsyncStream<CLLocation>.Continuation
  
  let locationStream: AsyncStream<CLLocation>
  
  init() {
    (locationStream, continuation) = AsyncStream<CLLocation>.makeStream()
  }

  private func push(location: CLLocation) {
    continuation.yield(location)
  }
}

Producer (LocationProvider) here has access to Continuation and consumers has access to AsyncStream instance via public property locationStream

you are correct and the shape of the current design is because AsyncStream was not formally Sendable for a few years, and we needed the Continuation to be Sendable. in my opinion, AsyncStream should have been the continuation, and AsyncStream.AsyncIterator should have been what we call AsyncStream today. but it is far too late to change that now.

1 Like