Skip to content

Instantly share code, notes, and snippets.

@akbashev
Forked from rjchatfield/Promised.swift
Last active August 29, 2022 18:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akbashev/eb9d4198b0182b156b076704bcb32d12 to your computer and use it in GitHub Desktop.
Save akbashev/eb9d4198b0182b156b076704bcb32d12 to your computer and use it in GitHub Desktop.
Promised values in Swift Concurrency
/// Wrapper around a lazily resolved using checked continuation.
///
/// Usage:
///
/// ```
/// let promiseA = Promised<String>()
/// async let a1 = promiseA.value
/// promiseA.resolve(with: "AAA")
/// let a2 = try await promiseA.value
/// print(try await a1, a2)
/// ```
public actor Promised<WrappedValue: Sendable> {
private var _result: Result<WrappedValue, any Error>?
private var observers: [CheckedContinuation<WrappedValue, any Error>] = []
public var value: WrappedValue {
get async throws {
switch _result {
case .success(let value):
return value
case .failure(let failure):
throw failure
case .none:
return try await withCheckedThrowingContinuation(add(continuation:))
}
}
}
public nonisolated func resolve(with value: WrappedValue) {
catching(.success(value))
}
public nonisolated func reject(with error: any Error) {
catching(.failure(error))
}
public nonisolated func catching(_ result: Result<WrappedValue, any Error>) {
Task { await _resolve(result: result) }
}
private func _resolve(result: Result<WrappedValue, any Error>) {
switch _result {
case .none:
self._result = result
for continuation in observers {
continuation.resume(with: result)
}
case .some:
break
}
}
private func add(
continuation: CheckedContinuation<WrappedValue, any Error>
) {
observers.append(continuation)
}
public init() {}
}
// Usefull when you just need to wait for some future void call (like viewDidLoad).
public extension Promised {
@discardableResult
func wait() async -> WrappedValue? {
return try? await self.value
}
}
// For running result on main thread just use Task with @MainActor, like:
// let promise: Promised<Void> = .init()
// Task { @MainActor in
// await promise.wait()
// animate()
// }
// promise.resolve(with: ())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment