Async/Await a source of constant misunderstanding

JasinCole

Well-known member
Joined
Feb 16, 2023
Messages
66
Programming Experience
1-3
I've been trying to wrap my head around this Async/Await pattern and how it is meant to be handled inside a SychronousContext when programming a GUI. There seems to be so many pieces and I think I am just confusing myself trying to research and implement ideas that i've actually hinderd my ability to learn anything meaningful.

As an example of my recent struggles, I am trying to build a dashboard that will make calls to a database using lots of different methods to gather the data and update my viewmodel properties.
What I would like to do is have an overall method that fires off a bunch of other asynchronous methods, when all those methods are finished and complete I want to access the results of those calls and update my viewmodel. For instance... My understanding is this approach will fire off each task and run them Asynchrounously. Then I await each task to completion and assign the result to the property.

C#:
public async Task LoadAccountBalancesAsync()
{
    var checkingTask = GetAccountAsync(int:<accountnum>);
    var savingsTask = GetAccountAsync(int:<accountnum>);
    var payrollTask = GetAccountAsync(int:<accountnum>);
    
    //Etc...
    
    Checking = await checkingTask;
    Savings = await //...
    Payroll = await //...
}

That would seem to work, but it's redudant and not very approachable when the amount of information I need is 3 to 4 times that amount or more.
What I'd like to do is
C#:
public async Task LoadAccountBalancesAsync()
{
    var tasks = Task[]
    {
        GetAccountAsync(int:<accountnum>);
        //etc...
    }
    
    await Task.WhenAll(task);
    
    //Update properties... But how?
}

Or even better would be to
C#:
public async Task LoadAccountBalances()
{
    var tasks = new List<Task>();
    
    foreach (var a in accounts)
    {
        tasks.Add(GetAccountAsync(a))
    }
    
    await Task.WhenAll(tasks);
    
    //But again how do I access the result of the task?
}

How would you do it and why would you use that approach? Is this true Asynchronization? I really like the idea of grouping that task together, `WhenAll()` to get a single state, that way I am not updating the dashboard with some valid and other non-valid date if an exception is thrown.

The next logical step in my brain would be the expand this further. Like so...
C#:
public async Task LoadDashBoard()
{
    //Run as Task Asynchronous
    var tasks = Task[]
    {
        LoadAccountBalances();
        LoadOutstandingAccountsReceivables();
        LoadOutstandingAccountsPayables();
        LoadJobOverUnder();
        LoadOustandingChangeOrders();
    }
    
    await Task.WhenAll(tasks);
}

Would appreciate a little guidance and some verbose thought process to why and how you would approach the problem.
 
This is an old question therefore I won't spend much time on it but I agree that async and await are difficult to understand. Perhaps this response will help others in the future.

One of the design requirements of C# is that it should be as easy to understand as possible and I think that async and await are a violation of that. My understanding is that C# was not the first to use async and await but I think they could have done it better. Perhaps it is just a matter of documenting how they work, I have yet to read a good explanation.

In C# we have the Thread class and the newer Task class. As best as I understand async and await, everything they are used for can be done with the Thread or Task class. Neither async nor await creates threads, the thread must be created by something else and I consider the Thread and Task classes to be easier to understand.
 
What do you find difficult to understand?

I do not know how to answer that. I feel as if I would need to understand it to know how I do not understand it. I am confident it would be easy for me to understand if someone explained it in a manner I consider to be clear. It is my impression that many developers have difficulty understanding async and await.

I understand how to do multitasking/multithreading in both Windows and IBM Mainframe (MVS operating system and later versions) systems. I am however unclear about async and await.

I do not want to distract this discussion but GPU is another example of something people tend to explain in terms I consider unclear. When I did find something clearly explaining what GPUs are, it is easy to understand and is very clear.
 
Last edited:
Async/await is about getting multiple things done in a unit of time by assuming that everything you're trying to do will eventually need to wait for something else so you can flip the doer to another task

Threading is about getting multiple things done in a unit of time by assuming everything you're trying to do will eventually need to wait for something else so you need as many doers as you have tasks so it doesn't matter if all of them are sitting around waiting

In an office context suppose you have 3 things to do in the next 5 minutes; make coffee, refill the printer, sanitise the water cooler. Making coffee involves a period f waiting for the water to boil. Refilling the printer involves a period of waiting for the printer to power up and recalibrate. Sanitising the water cooler involves waiting for the chemicals to drain down fully.
Assuming you have access to a pool of smart employees that can do any of these tasks

Async await is like hiring one employee, they set about making the coffee but when they reach the point where the kettle is turned on and there is nothing else to do but wait, they go start filling the printer instead. There has to be some way of saving the state they already proceeded to with the kitchen - getting the cup out etc - so it's not wasted effort. At the point they need to wait for the printer, maybe the kettle is boiled, so they could return to that or they could start the cooler. If the kettle wasn't boiled they could start the cooler and maybe by the time they need to wait for that, either kettle or the printer is done with its thing so they could return to that. Maybe nothing is ready so they just have to sit around and wait, or maybe they will go vacuum the floor or, or, or..
Eventually all the tasks will get done and pretty economically too; it only took one employee to do everything so that was a cheap hire

Threads is like hiring 3 employees, setting each of them going on a single task and just accepting that you're going to mostly pay them to stand around doing nothing but waiting.

You get the job done marginally quicker with 3 than 1 but it's 3x more expensive. A better strategy is to use as few people as possible; start with one and if they're overworked, hire another. The beauty there is mostly because they're universally capable and every job saves state in the job, employee 2 can finish off the coffee that employee 1 started because employee 1 didn't take the jar of coffee with them when they started the printer


Overall when you've got a thousand mostly-waiting things to do, it's better to do it with a handful of workers than dedicating a worker to each

Async all the way down is simply providing a path through your code for c# to follow, when it hits an await call it saves the state of your code, zips back up the call stack and out of your code, back to the matrix, and goes and chooses something else to be getting on with. Your db download completes or whatever, and your code task signals its ready to resume, eventually a thread will come back, restore the state and carry on from where it left off, probably straight into another await for a lather rinse repeat

Async await is all about getting more done with less, that's all..
 
If you are trying to understand async/await from the "what value does it have? how do I use this?" perspective the updated MS docs seems to be much better written than the old async/await documentation:
and provides a better supplement to the original overview:

There is also a much more recent article that talks about how async/await is implemented -- "how it works under the covers?" perspective:
 
Back
Top Bottom