How to (race) await an array of tasks?

There might be an easier way to do this, but I’m creating buttons in a for loop, and adding the widgets to a [widget]int map, and the OnClick() task to an []awaitable(widget_message) array.

I don’t know how I would await the tasks though. Completely expecting a compiler error I tried race(T:Tasks){T.Await()} which surprisingly didn’t give me any error messages, but when saving, the editor crashed, and crashes on startup unless I comment out the code.

Code Example
CreateUI(P:player, ButtonCount:int)<suspends>:int=
    Canvas := canvas{}
    var Tasks : []awaitable(widget_message) = array{}
    var ButtonMap : [widget]int = map{}
    if(PlayerUI:=GetPlayerUI[P]):
        for(I:=0..ButtonCount-1):
            Button := canvas_slot:
                Anchors := anchors:
                    Minimum:= vector2:
                        X:=0.1+0.05*I
                        Y:=0.25
                    Maximum:= vector2:
                        X:=0.1+0.05*I
                        Y:=0.25
                Widget := button_regular{}
            if(W:=button_regular[Button.Widget], set ButtonMap[W]=I):
                set Tasks += array{W.OnClick()}
            Canvas.AddWidget(Button)
        PlayerUI.AddWidget(Canvas, player_ui_slot{InputMode:=ui_input_mode.All})
    #It would be really great if the following line worked. It doesn't give any errors in vscode, but the editor crashes
    WM:=race(T:Tasks):
        T.Await()
    if(ButtonID:=ButtonMap[WM.Source]):
        return ButtonID
    return -1

I could make the Tasks array a tuple instead and do race{Tasks(0).Await(), Task(1).Await(), Task(2)...etc}, but then I would have to hardcode it, and couldn’t change how many buttons I create at runtime.

Is there a way to work around this issue currently?

Still don’t know why the editor crashes if I type race(T:Type){T.Await()}, but I figured out how to do it without an array of awaitables

Still not 100% happy with my solution, but this works:

ToMessage<localizes>(S:string):message="{S}"

MyUI := class:
    var Buttons : []button_regular = array{}
    Player : player

    #Is there any way to put this inside AwaitButtons()?
    AwaitThenSignal(A:awaitable(widget_message), S:signalable(int), I:int)<suspends>:void=
        A.Await()
        S.Signal(I)

    #Returns the index of the pressed button
    AwaitButtons()<suspends>:int=
        Event := event(int){}
        for(I->B:Buttons):
            spawn{AwaitThenSignal(B.OnClick(), Event, I)}
        return Event.Await()
    
    CreateUI(Labels:[]string):void=
        if(PlayerUI := GetPlayerUI[Player]):
            for(I->L:Labels):
                Button := canvas_slot:
                    Anchors :=anchor:
                        Minimum := vector2{X:=0.5, Y:=0.25+I*0.1}
                        Maximum := vector2{X:=0.5, Y:=0.25+I*0.1}
                    Widget := button_regular{DefaultText:=ToMessage(L)}
                Canvas.AddWidget(Button)
                if(BW := button_regular[Button.Widget]):
                    set Buttons += array{BW}
            PlayerUI.AddWidget(Canvas, player_ui_slot{InputMode:=ui_input_mode.All})

Hmm, seems like it should work, but it might be buggy somehow since I imagine it hasn’t been used a lot yet. Have you tried iterating using regular integers instead, like race(I := 0..Tasks.Length-1)?

I hadn’t tried that, but that also just crashes the editor when compiling, but thanks for the suggestion anyways!

The intent is that race(T:Tasks){T.Await()} will eventually do what expected, but it’s only partially implemented right now. The compiler front end accepts it, but then the back end produces a compile error as the codegen part hasn’t been implemented yet.

Unfortunately, there’s a bug in 24.30 that leads to back end compile errors crashing UEFN. I’ve committed a fix that should be in the next release.

Did this fix regress or possibly not make it in at all? The behavior of such a construct silently crashing the editor seems to still happen in 28.20.

Are there any better alternatives for awaiting arrays of tasks now?

how did you go?
my gamemanager will use multiple of a class, and I want to await them all before it proceeds to the results.