// // AsyncThrowingJustSequence.swift // // // Created by Thibault Wittemberg on 04/01/2022. // /// `AsyncThrowingJustSequence` is an AsyncSequence that outputs a single value from a throwing closure and finishes. /// If the parent task is cancelled while iterating then the iteration finishes before emitting the value. /// /// ``` /// let justSequence = AsyncThrowingJustSequence { 1 } /// for try await element in justSequence { /// // will be called once with element = 1 /// } /// ``` public struct AsyncThrowingJustSequence: AsyncSequence, Sendable { public typealias Element = Element public typealias AsyncIterator = Iterator let factory: @Sendable () async throws -> Element? public init(_ element: Element?) where Element: Sendable { self.factory = { element } } public init(factory: @Sendable @escaping () async throws -> Element?) { self.factory = factory } public func makeAsyncIterator() -> AsyncIterator { Iterator(factory: self.factory) } public struct Iterator: AsyncIteratorProtocol, Sendable { let factory: @Sendable () async throws -> Element? let isConsumed = ManagedCriticalState(false) public mutating func next() async throws -> Element? { guard !Task.isCancelled else { return nil } let shouldEarlyReturn = self.isConsumed.withCriticalRegion { isConsumed -> Bool in if !isConsumed { isConsumed = true return false } return true } if shouldEarlyReturn { return nil } let element = try await self.factory() return element } } }