Async/await style for UI in Unity

Viewed 2445

I want to implement async / await style in Unity for UI. It occurs to me that you can write a static class in which to define a method like this public static async Task<Button> WaitForButtonClickAsync(CancellationToken ct, params Button[] buttons) This method will wait for any of the transferred buttons to be pressed with Task.whenAny, and then it will return a button. But what’s after that? I can only come up with something like a giant switch-case block to figure out which button was pressed or pass the buttons with a callback to the method right away, but it seems to me that these are not the best solutions.

1 Answers

I've used this pattern very successfully. But you are right about the giant switch-case, it does require a bit of boilerplate. But then again, it's not actually any shorter with callbacks.

Example:

Button pressedButton = await WaitForButtonClickAsync(ct, button1, button2, button3);
if (pressedButton == button1) await DoStuff1(ct);    
if (pressedButton == button2) await DoStuff2(ct);
if (pressedButton == button3) await DoStuff3(ct);

Or with callbacks (Note that we aren't removing event handlers here, so this solution isn't at all shorter in reality), though here we don't get the advantage of chaining async code:

button1.onClick += () => DoStuff1();
button2.onClick += () => DoStuff2();
button3.onClick += () => DoStuff3();

You could also make some kind of helper to avoid the if-elses, something like this:

await WaitForButtonHandlersAsync(ct, 
                                (button1, DoStuff1), 
                                (button2, DoStuff2),
                                (button3, DoStuff3));

But as you start mixing in other awaits in addition to buttons, this can get messy, and I've always just fallen back to if-else listings.

EDIT: One thing to ad is that you can also group things. For example if you have a commonly used set of navigation buttons/gestures in addition to a few context specific buttons, you can bundle the navigation buttons into one task. This example also contains the usage of linked tokens presented in the video.

using (var cts = CreateLinkedTokenSource(ct))
{
    var navigationTask = WaitForNavigationEventAsync(cts.Token);
    var buttonTask1 = WaitForButtonClickAsync(cts.Token, button1);
    var buttonTask2 = WaitForButtonClickAsync(cts.Token, button2);

    var finishedTask = await Task.WhenAny(navigationTask, buttonTask1, buttonTask2);
    await finishedTask; // "Reveal" and propagate exceptions
    cts.Cancel();

    if (finishedTask == navigationTask) await Navigate(navigationTask.Result, ct);
    if (finishedTask == buttonTask1) await DoStuff1(ct);
    if (finishedTask == buttonTask2) await DoStuff2(ct);
}

With this kind of constructs, the listing never gets out of hands and you avoid some repeating code.

Related