Skip to content

Instantly share code, notes, and snippets.

@krzyzanowskim
Last active June 25, 2022 12:25
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save krzyzanowskim/d31fb8ae46bd5ef272245f960fbcc26f to your computer and use it in GitHub Desktop.
Save krzyzanowskim/d31fb8ae46bd5ef272245f960fbcc26f to your computer and use it in GitHub Desktop.
Synchronously (well) wait for async Task value update https://twitter.com/krzyzanowskim/status/1523233140914876416
/// Wait for async operation to return value and call callback with the value
/// This class is intended to workaround/simplify async/await + actors isolation
/// https://twitter.com/krzyzanowskim/status/1523233140914876416
private class AsyncWaiter<T> {
var didReceiveValue: Bool = false
let value: (T) -> Void
let operation: () async throws -> T
init(_ value: @escaping (T) -> Void, operation: @escaping () async throws -> T) {
self.value = value
self.operation = operation
}
func wait() {
Task.detached {
do {
self.value(try await self.operation())
self.signal()
} catch {
self.signal()
throw error
}
}
while !didReceiveValue {
RunLoop.current.run(mode: .default, before: .distantFuture)
}
}
func signal() {
didReceiveValue = true
}
}
extension Task where Success == Void, Failure == CancellationError {
struct ValueError: LocalizedError {
var errorDescription: String? {
"Didn't receive asynchronous value."
}
}
public static func wait<T>(operation: @escaping () async throws -> T) throws -> T {
var v: T? = nil
AsyncWaiter({
v = $0
}, operation: operation).wait()
if let v = v {
return v
} else {
throw ValueError()
}
}
}
let result: [String] = try Task.wait {
try await foo.getValues()
}
print(result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment