You are using async-await in a way that was not intended.
This interview with Eric Lippert helped me to understand async-await. Search somewhere in the middle for async-await.
He compared async-await with a cook having to cook breakfast. After he put on the kettle to boil water for the tea he could decide not to do anything, until the water boils, after which he makes the tea and start toasting bread. Don't do anything until the bread has been toasted, etc.
It would be much faster if instead of doing nothing, whenever the cook would be waiting for another process to end he would start doing other things instead, before waiting for the first thing to complete:
Start boiling water
Start toasting bread
wait until the water boils
use the boiling water to start making tea
...
You'll see async-await typically in situations where a thread orders some process to do something that takes some time that the thread can't do itself: fetch data from a database, write data to a file, fetch data from the internet: actions where the thread can't do anything but wait.
You start your procedure correctly. Your cook does some preparations, but then he decides he should hire another cook to do the work for him while your cook does nothing but wait until the other cook finished working. Why isn't your cook doing this?
A more typical async-await would be like follows:
async void buttonStart_Click(object sender, EventArgs e)
{
buttonStart.Enabled = false;
await LoginAsync();
await CallProjectAsync();
await TriggerTask1Async();
await TriggerTask2Async();
buttonStart.Enabled = false;
}
Normally this would work if you used objects that support async-await. For every async function you know that somewhere there is a call to an awaitable async function. In fact your compiler will complain if you declare your function async without awaiting any call.
Alas, sometimes your cook can't depend on a process to continue while your cook is doing other things. The reason for this is because the WebBrowser class does not support async-await like the WebClient class does.
This is similar to times like when your cook has to do some heavy processing, while you still want to keep your procedure async-await, like slicing tomatoes: the kettle will boil the water automatically, you need another cook to slice the tomatoes.
That is typically the case when in an async-await scenario you would use Task.Run. In your case, this is for instance within your login function
async void LoginAsync()
{
// still main thread. Notify the operator:
labelProgress.Text = "logging in";
// we start something that takes some time that this cook can't spend:
// let another cook do the logging in
await Task.Run( () =>
{
wb.Navigate(Settings.Default.LoginUrl);
WebBrowserTools.Wait(wb);
wb.Document.GetElementById("username").InnerText = Settings.Default.LoginUsername;
wb.Document.GetElementById("password").InnerText = Settings.Default.LoginPassword;
wb.Document.Forms[0].InvokeMember("submit");
WebBrowserTools.Wait(wb);
}
// back in main thread:
labelProgress.Text = "logged in successfully";
}
So the trick is: use as often async-await prepared classes as possible. Only if your cook has to do something lengthy but he wants to be free for other things, do Task.Run(). await when your cook needs to be certain that the other Cook finished its job. Don't await while you have other things to do. Let your cook be the one who handles the user interface.