Last active
August 29, 2022 18:50
-
-
Save rjchatfield/d5ef79c7bc4bbdf4cb0e170ebd68e382 to your computer and use it in GitHub Desktop.
Promised values in Swift Concurrency
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Wrapper around a lazily resolved using checked continuation. | |
/// | |
/// Usage: | |
/// | |
/// ``` | |
/// let promiseA = Promised<String>() | |
/// async let a1 = promiseA.value | |
/// promiseA.resolve("AAA") | |
/// let a2 = await promiseA.value | |
/// print(await a1, a2) | |
/// ``` | |
public actor Promised<WrappedValue: Sendable> { | |
private var _value: WrappedValue? | |
private var observers: [CheckedContinuation<WrappedValue, Never>] = [] | |
public init() {} | |
// MARK: - | |
public var value: WrappedValue { | |
get async { | |
if let _value = _value { | |
return _value | |
} else { | |
return await withCheckedContinuation(add(continuation:)) | |
} | |
} | |
} | |
public nonisolated func resolve(_ value: WrappedValue) { | |
Task { | |
await _resolve(value: value) | |
} | |
} | |
// MARK: - | |
private func _resolve(value: WrappedValue) { | |
guard _value == nil else { | |
return | |
} | |
_value = value | |
for continuation in observers { | |
continuation.resume(returning: value) | |
} | |
} | |
private func add(continuation: CheckedContinuation<WrappedValue, Never>) { | |
observers.append(continuation) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Wrapper around a lazily resolved using checked continuation. | |
/// | |
/// Usage: | |
/// | |
/// ``` | |
/// let promiseA = PromisedOnMain<String>() | |
/// async let a1 = promiseA.value | |
/// promiseA.resolve("AAA") | |
/// let a2 = await promiseA.value | |
/// print(await a1, a2) | |
/// ``` | |
public final class PromisedOnMain<WrappedValue: Sendable> { | |
@MainActor | |
private var _value: WrappedValue? | |
@MainActor | |
private var observers: [CheckedContinuation<WrappedValue, Never>] = [] | |
public init() {} | |
// MARK: - | |
@MainActor | |
public var value: WrappedValue { | |
get async { | |
assert(Thread.isMainThread) | |
if let _value = _value { | |
return _value | |
} else { | |
return await withCheckedContinuation { continuation in | |
observers.append(continuation) | |
} | |
} | |
} | |
} | |
public func resolve(_ value: WrappedValue) { | |
Task { @MainActor in | |
_resolve(value: value) | |
} | |
} | |
// MARK: - | |
@MainActor | |
private func _resolve(value: WrappedValue) { | |
guard _value == nil else { | |
return | |
} | |
_value = value | |
for continuation in observers { | |
continuation.resume(returning: value) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Ah, thx, was lazy to write from scratch and found your gist 😅
Updated for myself a bit though (with Result usage and throws):
https://gist.github.com/akbashev/eb9d4198b0182b156b076704bcb32d12