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})

2 Likes

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)?

1 Like

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.

3 Likes

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?

2 Likes

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.