[Pre-Pitch] AsyncValue

While migrating a project to the new async features of Swift I constantly needed a mechanism for a synchronization point based of the availability of some value. As far as I can tell, currently there is nothing in the stdlib to cover this. Something like:

public actor AsyncValue<T> {
    private var state: State
    public init() {
        self.state = .waiting
    public var value: T {
        get async {
            switch state {
            case .waiting:
                return await withCheckedContinuation { continuation in
                    let continuations = [continuation]
                    state = .valueRequested(continuations)
            case .valueReceived(let value):
                return value
            case .valueRequested(let continuations):
                return await withCheckedContinuation { continuation in
                    let continuations = continuations + [continuation]
                    state = .valueRequested(continuations)
    public func set(_ value: T) async {
        switch state {
        case .waiting, .valueReceived:
            state = .valueReceived(value)
        case .valueRequested(let continuations):
            for continuation in continuations {
                continuation.resume(returning: value)
            state = .valueReceived(value)

extension AsyncValue {
    enum State {
        case waiting
        case valueReceived(T)
        case valueRequested([CheckedContinuation<T, Never>])

Basically this is something like an 'AsyncSequence' with only 1 value.

Is it just me, or do you guys also needed something like that while migrating?



Sounds pretty useful to me, I would welcome that in the standard library.

(But… does that code work yet for you? There seem to be some open issues with resuming stored continuations from within actor contexts, that‘s why I’m asking)

This very much looks like an (unstructured) Future to me, with the special feature that you can change the value.

I think the authors of Swift Concurrency have said at multiple occasions that they do not want to provide general Futures. So unless I'm mistaken what you need to do here is to provide compelling examples of why this is needed :+1:

Yes, works for me

I agree, this is pretty much like a Future. I think the authors of the Swift concurrency model are right about not taking Futures as the default mechanism for concurrency since it simplifies 99% of the cases where a handle is not needed.

But sometimes you need a handle: whenever the async event can happen before or after somebody requests the value you need a handle / or when a request for an async value is dependent of another async event that is not part of the current context. That is sometimes the case, and that's why I think an AsyncValue in the stdlib would be worth it.

I encountered the problem while dealing with UI flows where some screen was dependent of the user input of another screen.

So you can wait on a task handle. I guess that's the most Future-like thing that is currently available.

Please give some more concrete example, so it's more convincing that you need a Future instead of e.g. waiting on a task handle :+1:

I probably just missed that feature of the Task API (which would make this pitch obsolete). Can you provide a hint which Task API would provide that functionality? I kinda lost track since it changed so much...

It's something like:

let handle = Task { await some_work_here() }
let result = await handle.value

Thanks - but how do you provide an async value to the Task from outside the closure?
(like the public func set(_ value: T) async part of my example above)

Right. So this is not a Future :+1:

Then my pitch is not obsolete :wink:

1 Like

I agree. You just really need some concrete examples arguing for why this is needed :+1:

Your implementation is very nice, but what is needed here are concrete examples.

Terms of Service

Privacy Policy

Cookie Policy