Execute async command before navigating back - c#

I am using Modern UI for WPF template in a .NET 4.0 app where one page needs to execute an async command before it navigates back to another page. At the same time, UI thread must be unlocked while command is running. If I do this:
public void OnNavigatingFrom(FirstFloor.ModernUI.Windows.Navigation.NavigatingCancelEventArgs e)
{
TaskEx.Run(() =>
{
//Replace Sleep call by the async command execution
System.Threading.Thread.Sleep(5000);
}).Wait();}
The OnNavigatingFrom waits before navigating back, but the UI is blocked during this time.
Any ideas on how to execute the async code in a different context and make OnNavigatingFrom runs "synchronously"?
EDIT: There is a similar thread with a workaround and no conclusive answer.

one page needs to execute an async command before it navigates back to another page
This is the problem. User interfaces are not asynchronous, period. You can't just stick an asynchronous operation into the mix while the UI is doing a transition.
Think about it this way: as a user, if you decide to navigate back, and there's a network hiccup or something and the program freezes for a few seconds while navigating back, that's a bad user experience, right? The UI simply can't wait for asynchronous work during an update.
What you can do is do the work before the user is allowed to navigate back. You can, for example, capture the user's intent to navigate back and show some kind of "please wait" UI while the operation is going, and then after the operation completes do the actual navigation.

Here is what you need to do:
public void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
// Fire and forget.
Task.Run(() => MyCommandAsync());
}
The above is a fire and forget approach, instead it is preferred to use async and await:
public async void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
await MyCommandAsync();
}

Just await the Task instead of using .Wait().
See Using async-await on .net 4 for how to await in .NET 4.0

Use OnNavigatingFrom to do your stuff. e.Cancel prevent going back. Use Frame.GoBack() after your operation has finished.
protected override async void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
if (Current.HasModifications())
{
e.Cancel = true;
await Current.Save().ContinueWith((t) =>
{
//Go Back after the Operation has finished
Frame.GoBack();
}, TaskScheduler.FromCurrentSynchronizationContext());
}
else
{
base.OnNavigatingFrom(e);
}
}

Related

Webforms - update UI while waiting for long running task

I'm using c#/webforms for a form. The form also relies on UpdatePanels.
On the final step the event launches a long running (90 secs) HttpPost to a third party site.
At present when the submit button is pressed, I present a modal overlay while the process takes place, then redirect the user to another page on completion.
This is okay, but it doesn't refresh the update panel during the process, so the UI doesn't reflect the true picture - it's a step behind.
I've only just moved from Asp.Net 4.0 so not had the opportunity to investigate async await stuff until now.
I'm testing a new process but can't get it to work the way I want.
Here's the event that's called on the final step:
protected async void Submit_Click(object sender, EventArgs e)
{
if (Page.IsValid)
{
Task<bool> post = PostApplication();
var step = Int32.Parse(((LinkButton)sender).CommandArgument);
SetStepState(step, StepState.Complete);
upStepper.Update();
var result = await PostApplication();
Response.Redirect("/Result.aspx");
}
}
public async Task<bool> PostApplication()
{
await Task.Delay(3000);
return true;
}
The task does its job properly and a result is returned in 3 seconds, followed by the redirect.
What is not happening is the UI isn't being updated during the wait. The method SetStepState() updates a data-attribute on the current step, which in turn should change how the step displays, but it doesn't. I've also forced an update to the update panel immediately after changes but nothing changes.
I'm not sure whether what I'm attempting is possible so would appreciate any advice.

Winforms call to async method hangs up program

I have been working around this problem for a while, but now I would really like to understand what goes wrong. I have a rather simple application (it's a turtoise SVN plugin for youtrack, but I can reproduce the problem with a trivial winforms app).
I have an async method ResolveIssue
public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
await Task.Delay(1000);
return true;
}
All I have to do to create a deadlock is call this async method in a Button event handler, and call Task.Wait or Task.Result, like this
private void buttonOk_Click(object sender, System.EventArgs e)
{
var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
if (asyncResolvedIssue.Result) {} // <== deadlock!
}
Now I understand it's rather weird to have an async method and actively wait for it, but why would it generate a deadlock?!
Your problem is because you are blocking the UI thread when you call .Result and you told the continuation after Task.Delay to run on the UI thread. So you are blocking the UI waiting for a task that is blocked on waiting for the UI to become free, a classic deadlock.
Two solutions. First make the button click async too.
private async void buttonOk_Click(object sender, System.EventArgs e)
{
var asyncResolvedIssue = api.ResolveIssue(issue, revision, pathList);
if (await asyncResolvedIssue) {} // <== no deadlock!
}
Event handlers are the only place you are allowed to do async void.
The other option is tell Task.Delay it does not need to have the rest of its function run on the UI thread by setting ConfigureAwait(bool) to false.
public async Task<bool> ResolveIssue(Issue issue, int revision, string[] pathList)
{
await Task.Delay(1000).ConfigureAwait(false);
return true;
}
Now the line of code after the Task.Delay will run on a threadpool thread instead of the UI thread and will not be blocked by the fact that the UI thread is currently blocked.

C#/.NET 4.5 - Why does "await Task.WhenAny" never return when provided with a Task.Delay in a WPF application's UI thread?

Given the following code, why does ask.WhenAny never return when provided with a Task.Delay of 1 second? Technically I'm not sure if it does return after a extended amount of time, but it doesn't after 15 seconds or so after which I manually kill the process. According to the documentation I shouldn't be required to manually start the delayTask, and in fact I receive a exception if I try to do so manually.
The code is being called from the UI thread when a user selects a context menu item in a WPF application, although it works fine if I have the click method specified for the context menu item run this code in a new thread.
public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
...
SomeMethod();
...
}
public void SomeMethod()
{
...
SomeOtherMethod();
....
}
public void SomeOtherMethod()
{
...
TcpClient client = Connect().Result;
...
}
//In case you're wondering about the override below, these methods are in
//different classes i've just simplified things here a bit so I'm not posting
//pages worth of code.
public override async Task<TcpClient> Connect()
{
...
Task connectTask = tcpClient.ConnectAsync(URI.Host, URI.Port);
Task delayTask = Task.Delay(1000);
if (await Task.WhenAny(connectTask, delayTask) == connectTask)
{
Console.Write("Connected\n");
...
return tcpClient;
}
Console.Write("Timed out\n");
...
return null;
}
If I change ContextMenuItem_Click to the following it works fine
public void ContextMenuItem_Click(object sender, RoutedEventArgs e)
{
...
new Thread(() => SomeMethod()).Start();
...
}
I predict that further up your call stack, you're calling Task.Wait or Task<T>.Result. This will cause a deadlock that I explain in full on my blog.
In short, what happens is that await will (by default) capture the current "context" and use that to resume its async method. In this example, the "context" is the WPF UI context.
So, when your code does its await on the task returned by WhenAll, it captures the WPF UI context. Later, when that task completes, it will attempt to resume on the UI thread. However, if the UI thread is blocked (i.e., in a call to Wait or Result), then the async method cannot continue running and will never complete the task it returned.
The proper solution is to use await instead of Wait or Result. This means your calling code will need to be async, and it will propagate through your code base. Eventually, you'll need to decide how to make your UI asynchronous, which is an art in itself. At least to start with, you'll need an async void event handler or some kind of an asynchronous MVVM command (I explore async MVVM commands in an MSDN article). From there you'll need to design a proper asynchronous UI; i.e., how your UI looks and what actions it permits when asynchronous operations are in progress.

How do async in window phone?

In app.xaml.cs
private void Application_Closing(object sender, ClosingEventArgs e)
{
A.onEndApp();
}
In class A
async public static void onEndApp()
{
string temp=await doSomething();
//code here
}
I have a problem,when I close an app,and then onEndApp() method is run,
when doSomething() run complete and {//code here} is not run,but if I put A.onEndApp() in
another method it run normaly,for example Application_Launching() method,it will be run
{//code here}
I think when app is running it no problem,but when app is closing it run await complete and then stop,I want to run complete method when I close app in async.
After Application_Closing is executed (synchronously), the OS will terminate the process. There might be a short delay before it does so, and that might be enough for you to finish writing to isolated storage, or it might not in which case you'll end up having corrupted state
I'll just take a direct quote from "Beware the perils of async/await in application lifecycle event handlers (in fact in any event handlers)" on Andy Wigley's blog.
Calling async code from Application_Deactivated or Application_Closing
The guidance here is “don’t'”. If you write your apps carefully, you can be saving changes to persistent data as you go along, so you shouldn’t have anything to do in the application lifecycle events.
If you must, you can try doing something like this:
SomeAsyncMethod().AsTask().Wait()
If the operation completes within the timeout period AND it doesn’t deadlock due to needing
to pump the UI thread, it will work… but don’t count on it.
Try this:
private void Application_Closing(object sender, ClosingEventArgs e)
{
var task = A.onEndApp();
task.Wait();
}
async public static Task onEndApp()
{
string temp = await doSomething();
//code here
}
Otherwise, you're effectively just spinning off a thread that will never get a chance to run to completion, because the application is closing out from underneath it, and the thread will get terminated before it can run.

Something is going wrong with my EventWaithandle, it doesn't change to signaled?

So I'm working on a windows 8 application with some asynchronous methods.
In one particular place I need the aplication to wait for the async method to finish, but it doesn't seem it sends the EventHandle it's state.
Here are the methods that need to work together:
public class Film : Page
private User loggedinUser = new User();
private EventWaitHandle handle = new AutoResetEvent(false);
private dynamic parameters;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.parameters = e.Parameter;
LoadFacebookData(parameters);
handle.WaitOne();
LoadUserMovies(loggedinUser.UserName);
}
private async void LoadFacebookData(dynamic parameter)
{
//async code that gets info from facebook whichs determines what user is logged in
handle.Set();
}
private void LoadUserMovies(string username)
{
// irrelevant code
}
the moment the code hits the handle.WaitOne() bit, it stops working completely
In short you are not supposed to use async and wait on something. The standard mistake is to wait on the task returned (deadlock). You did a variation on this: You created an event and waited on it (also deadlock).
Solution: Either embrace async-await or don't do async at all. Can't do both in a mixed style (generally).
If await is available to you, this is a good start:
await LoadFacebookData(parameters);
Get rid of the event.
I suggest you to use the
await LoadFacebookData(parameters);
code line to get the execution continue on the same thread it was started once your async operation finishes.
As far as I can see, there is nothing wrong with the code you have posted. This leads me to believe that the problem is in the code you have commented away. The problem is probably that your code-execution never reaches the line
handle.Set();
This can be because of an exception, or your async code simply never completes. To "deal" with the possibility of an exception, you should probably use the builtin Wait-method of the Task-object, rather than your own EventWaitHandle.
You can remove the EventWaitHandle as a whole, and simply do:
LoadFacebookData(parameters).Wait();
This will propagate any exceptions that occur in the thread within.

Categories

Resources