Will array of async values be possible?

The async let bindings are quite handy when you have one or two, but it is not possible to have an array of them.

I wish I could to this:

let fv: [() async -> Int]  = [f, g, h]
let uv: [async Int] = [] // <--- not possible

for f in fv {
    async let u = f ()
    uv.append (u)
}

for u in uv {
    await print (u)
}

There is a workaround, but it involves explicitly spawning tasks.

let fv: [() async -> Int]   = [f, g, h]
var tv: [Task <Int, Never>] = []

for f in fv {
    let t = Task {
        await f ()
    }
    tv.append (t)
}

for t in tv {
    await print (t.value)
}

I am wondering if this will be possible one day?

1 Like

In theory it would be possible I suppose but the resulting array would have to be nonescapable and it might have surprising semantics since removing an element form such array cannot remove the value from the scope — we still MUST await on it. So it could be a bit weird to use in practice… implementation might be tricky but doable.

So the question is if this is solving enough problems to warrant the complexity?

Right now I think we have bigger fish to fry, in making task groups easier to use etc. but maybe someday

5 Likes

I don't know about the standard library… but you might be able to use AsyncSyncSequence in a creative way to unblock your use-case.

1 Like

Isn’t this just a task group? (Potentially with some sort of wrapper that buffers values so that they come back in order)

let fv: [() async -> Int]  = [f, g, h]
await withTaskGroup(of: Int.self) { group in
    for f in fv {
        group.addTask { await f() }
    }

     for u in group {
        await print (u)
    }
}
1 Like

Not an answer to your question (whether it will become a language feature) but the pattern potentially can become a part of the standard library. I have a utility struct called Zip in my library that serves this purpose in case of homogenous actions, and a family of functions zip(...) for non-homogenous ones, see AsyncMux/AsyncMux/Sources/Zip.swift at main · crontab/AsyncMux · GitHub

Example use:

let fv: [() async -> Int]  = [f, g, h]

let results = await Zip(actions: fv)
    .result

results.forEach { print($0) }

(although I only have a throwing version of the above, but you get the idea)

1 Like

This sounds like just a parallel async map. I agree we should have an API to do that, implemented on top of task groups.

10 Likes