diff --git a/Sources/AsyncDataLoader/Channel.swift b/Sources/AsyncDataLoader/Channel.swift new file mode 100644 index 0000000..163220f --- /dev/null +++ b/Sources/AsyncDataLoader/Channel.swift @@ -0,0 +1,55 @@ +actor Channel: Sendable { + private var waiters = [Waiter]() + private var result: Result? +} + +typealias Waiter = CheckedContinuation + +extension Channel { + @discardableResult + func fulfill(_ success: Success) -> Bool { + guard result == nil else { + return true + } + + result = .success(success) + + while let waiter = waiters.popLast() { + waiter.resume(returning: success) + } + + return false + } + + @discardableResult + func fail(_ failure: Failure) -> Bool { + guard result == nil else { + return true + } + + result = .failure(failure) + + while let waiter = waiters.popLast() { + waiter.resume(throwing: failure) + } + + return false + } + + var value: Success { + get async throws { + try await withCheckedThrowingContinuation { continuation in + Task { + switch result { + case let .success(success): + continuation.resume(returning: success) + case let .failure(failure): + continuation.resume(throwing: failure) + case nil: + waiters.append(continuation) + } + } + } + } + } +} diff --git a/Sources/AsyncDataLoader/Channel/Channel.swift b/Sources/AsyncDataLoader/Channel/Channel.swift deleted file mode 100644 index 0950bf9..0000000 --- a/Sources/AsyncDataLoader/Channel/Channel.swift +++ /dev/null @@ -1,55 +0,0 @@ -actor Channel: Sendable { - private var state = State() -} - -extension Channel { - @discardableResult - func fulfill(_ value: Success) async -> Bool { - if await state.result == nil { - await state.setResult(result: value) - - for waiters in await state.waiters { - waiters.resume(returning: value) - } - - await state.removeAllWaiters() - - return false - } - - return true - } - - @discardableResult - func fail(_ failure: Failure) async -> Bool { - if await state.failure == nil { - await state.setFailure(failure: failure) - - for waiters in await state.waiters { - waiters.resume(throwing: failure) - } - - await state.removeAllWaiters() - - return false - } - - return true - } - - var value: Success { - get async throws { - try await withCheckedThrowingContinuation { continuation in - Task { - if let result = await state.result { - continuation.resume(returning: result) - } else if let failure = await self.state.failure { - continuation.resume(throwing: failure) - } else { - await state.appendWaiters(waiters: continuation) - } - } - } - } - } -} diff --git a/Sources/AsyncDataLoader/Channel/State.swift b/Sources/AsyncDataLoader/Channel/State.swift deleted file mode 100644 index ee5e784..0000000 --- a/Sources/AsyncDataLoader/Channel/State.swift +++ /dev/null @@ -1,25 +0,0 @@ -typealias Waiter = CheckedContinuation - -actor State { - var waiters = [Waiter]() - var result: Success? - var failure: Failure? -} - -extension State { - func setResult(result: Success) { - self.result = result - } - - func setFailure(failure: Failure) { - self.failure = failure - } - - func appendWaiters(waiters: Waiter...) { - self.waiters.append(contentsOf: waiters) - } - - func removeAllWaiters() { - waiters.removeAll() - } -}