/*:#localized(key: "WhenAStreamCanFail")
 # When a stream can fail: AsyncThrowingStream

 Real sensors disconnect. A stream that can *fail* (not just end) is an
 **`AsyncThrowingStream`**. It works exactly like `AsyncStream`, with two
 differences: you can finish it with an error, and the consumer loops with
 `for try await` inside a `do-try-catch`.
 */
//#-editable-code
import Foundation
import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

enum SensorFault: Error { case disconnected }

let readings = AsyncThrowingStream<Double, Error> { continuation in
    Task {
        continuation.yield(21.4)
        try? await Task.sleep(for: .milliseconds(300))
        continuation.yield(22.1)
        try? await Task.sleep(for: .milliseconds(300))
        // The sensor drops off the network.
        continuation.finish(throwing: SensorFault.disconnected)
    }
}

Task {
    do {
        for try await celsius in readings {
            print("🌡️ \(celsius)°C")
        }
        print("Finished cleanly")   // not reached in this example
    } catch {
        print("⚠️ Stream failed: \(error)")
    }
    PlaygroundPage.current.finishExecution()
}
//#-end-editable-code
/*:#localized(key: "WhenAStreamCanFail-2")
 **Watch it run:** two readings print, then `finish(throwing:)` ends the stream
 *with* an error. That error is thrown out of the loop, skips `Finished cleanly`,
 and lands in `catch`, printing `Stream failed: disconnected`. The `try` in
 `for try await` is what gives that error somewhere to go; leave it out and the
 compiler will remind you.

 * callout(Try it): Swap `finish(throwing: SensorFault.disconnected)` for a plain
 `finish()`. The loop now falls through to `Finished cleanly` and `catch` never runs.
 Ending and failing are two different exits.

 Reach for `AsyncThrowingStream` when the source can genuinely error out. If it can
 only ever *end*, plain `AsyncStream` keeps the consumer simpler: no `do-try-catch`
 required.

 * callout(The bigger picture): `AsyncStream` is the modern answer to the old "here's
 another value" patterns: delegate methods that fire repeatedly, `NotificationCenter`
 observers, completion handlers called more than once. Instead of scattering that
 logic across callback methods, you wrap the source once and let anyone consume it
 with a plain `for await` loop. Bridging a *single* callback into `await` is the next
 chapter's job: continuations.

 [Previous](@previous)  |  page 6 of 7  |  [Next: Wrapping Up](@next)
 */
