Async method is working as syncronous mehtod - c#

I am trying to call a async method of ViewModel from another method in View but it is behaving as syncronous.
View Model:
public async Task<bool> GetReadyForUnlockWithQR()
{
try
{
SmilaHash = GetRandomeHashKey();
var data = JsonConvert.SerializeObject(GetSmilaInfo());
var res = await apiClient.PostAsync<String>("readyforunlockwithqr", data);
if (res != null)
{
JObject json = JObject.Parse(res);
if (json["doUnlock"] != null)
{
LoginStatus = json.SelectToken("doUnlock").Value<bool>();
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
CancelPendingRequests();
throw ex;
}
return false;
}
I have my api methods defined in a custome APIClient file. The above request may take a minute to complete. I don't want to stop the UI and perform my further operations in View. Following is my View:
private async void UnlockButton_Click(object sender, RoutedEventArgs e)
{
try
{
await ViewModel.GetReadyForUnlockWithQR();
DisplayQRCode();
}
catch(Exception ex)
{
if (ex is HttpRequestException)
{
Debug.WriteLine("Server not reachable");
MessageBox.Show("Server not reachable");
}
else if (ex is OperationCanceledException)
{
Debug.WriteLine("Timeout exception");
QRCodeImage.Source = null;
QRCodeCanvas.Visibility = Visibility.Hidden;
}
else
{
Debug.WriteLine(ex.Message);
}
}
}
I above code ideally the DisplayQRCode() function should work immediately after await ViewModel.GetReadyForUnlockWithQR(); but it is not happening. The DisplayQRCode() is waiting to receive response from ViewModel.GetReadyForUnlockWithQR() Why is this not behaving as logical asyn code.

The DisplayQRCode() is waiting to receive response from ViewModel.GetReadyForUnlockWithQR() Why is this not behaving as logical asyn code.
The asynchronous method is behaving serially (not "synchronously"), which is exactly what await is supposed to do.
You can think of await as "asynchronous wait": the method is paused and will not continue pass the await until the task completes, but it waits asynchronously, so the thread is freed (the method returns to its caller).
I above code ideally the DisplayQRCode() function should work immediately after await ViewModel.GetReadyForUnlockWithQR(); but it is not happening.
If you want to do that, then you can call GetReadyForUnlockWithQR but don't await the task until after DisplayQRCode completes:
var getReadyTask = ViewModel.GetReadyForUnlockWithQR();
DisplayQRCode();
await getReadyTask;

Related

C# HttpClient PostAsync blocks forever

I have a strange behavior that I can't manage to explain.
In an async function, an awaited call blocks forever.
Note: it seams that the problem occurs since I moved from a console app to a Windows Form. (the call is called from the constructor of Form1().
_client is the HttpClient dotnet class.
public async Task GetConfigurationFile()
{
var stringContent = new StringContent(JsonConvert.SerializeObject(companyKey), Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
// This call works and returns the respons after a few milliseconds
response = _client.PostAsync(_configurationFileEndpoint, stringContent).Result;
// The same awaited call block forever and never returns.
response = await _client.PostAsync(_configurationFileEndpoint, stringContent);
}
public Form1()
{
InitializeComponent();
_engine = new Engine();
}
public Engine()
{
// Logic similar to this.
Configuration configuration = null;
try
{
using (var reader = new StreamReader(Directory.GetCurrentDirectory() + "/configuration.json"))
{
configuration = Newtonsoft.Json.JsonConvert.DeserializeObject<Configuration>(reader.ReadToEnd());
}
}
catch (Exception ex)
{
// Something done
}
_apiCall = new PlatformCommunication(configuration);
if (configuration == null)
{
try
{
_apiCall.GetConfigurationFile().Wait();
}
catch (Exception exc)
{
}
}
}
You are doing this:
_apiCall.GetConfigurationFile().Wait();
As explained in many places, such as here - blocking on async code from UI thread is bad idea. When you have this:
response = await _client.PostAsync(_configurationFileEndpoint, stringContent);
the SynchronizationContext will be captured before await and execution after await will continue on that context, which means in this case on UI thread. It cannot continue there, because UI thread is blocked by GetConfigurationFile().Wait(), so you have deadlock.
When you have this:
response = _client.PostAsync(_configurationFileEndpoint, stringContent).Result;
The code inside PostAsync uses ConfigureAwait(false) on every async call, to prevent continuations running on captured context. So all continuations run on thread pool threads and you can get away with blocking on async call with Result in this case (doesn't make it good idea still). Then after this change your GetConfigurationFile becomes synchronous (there is no await left), so you can get away with Wait() also.
You can do the same ConfigureAwait(false):
response = await _client.PostAsync(_configurationFileEndpoint, stringContent).ConfigureAwait(false);
And it will help in this case, but that's not the way to solve this problem. The real way is to just not block on async code on UI thread. Move _apiCall.GetConfigurationFile() outside of constructor.
#YK1: to prevent blocking calls, I can move the code in the
constructor of Engine() to an Async Initialize function and await
_apiCall.GetConfigurationFile() instead of_apiCall.GetConfigurationFile().Wait(); But then in my Winform, I
need to await engine.Initialize() from an Async function which I don't
have? ( engine must run automatically, not be behind a start button),
reason why I put it in the constructor of the form which is not async.
Instead of constructor, move your startup code code to an async method. You can subscribe to Form_Load event and call that method.
class Engine
{
public async Task Init()
{
// Logic similar to this.
Configuration configuration = null;
try
{
using (var reader = new StreamReader(Directory.GetCurrentDirectory() + "/configuration.json"))
{
configuration = Newtonsoft.Json.JsonConvert.DeserializeObject<Configuration>(reader.ReadToEnd());
}
}
catch (Exception ex)
{
// Something done
}
_apiCall = new PlatformCommunication(configuration);
if (configuration == null)
{
try
{
await _apiCall.GetConfigurationFile();
}
catch (Exception exc)
{
}
}
}
}
and
private async void Form_Load(object sender, EventArgs e)
{
_engine = new Engine();
await _engine.Init();
}

Cancell task with parameter

I do have kind of endless loop where I do some operations. I would like to be able to cancell current opration with specific reason. However I cannot find a solution to cancell task with a paremetr.
private async void ProcessAsync()
{
while (true)
{
try
{
var result = await Task<object>.Run(async () =>
{
await Task.Delay(10000);
await Task.Delay(10000);
return new object();
}, _cts.Token);
}
catch (OperationCanceledException)
{
}
//catch (AnyOtherException)
//{
//
//}
}
}
Whenever I want to cancell task I call CancellationTokenSource.Cancel() method. The method does not accept any arguemnts. All I can do is to catch OperationCanceledException.
Is it another way to solve that or another way to achive what I want?

How to wait for HttpClient GetAsync call until it returns the request in C#

I am trying to get data by the HttpClient. Data vary in size, it could be from few bytes to megabyte. I noticed many times my application exist even before it returns from the GetAsync. How can I wait until GetAsync complete it call? From the main app:-
backup.DoSaveAsync();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.BackgroundColor = ConsoleColor.Red;
// My app exist by printing this msg, wihout getting any data.
// someitmes it gets data and other times it gets notinng.
// I used sleep to wait to get the call completed.
Console.WriteLine("\nBackup has done successfully in SQL database")
public async void DoSaveAsync()
{
using (var client = GetHttpClient(BaseAddress, path, ApiKey))
{
Stream snapshot = await GetData(client, path);
if (snapshot != Stream.Null)
{
snapshot.Position = 0;
SaveSnapshot(snapshot);
}
}
}
private async Task<Stream> GetData(HttpClient client, string path)
{
HttpResponseMessage response = null;
try
{
response = await client.GetAsync(path);
System.Threading.Thread.Sleep(5000);
if (response.IsSuccessStatusCode == false)
{
Console.WriteLine($"Failed to get snapshot");
return Stream.Null;
}
return await response.Content.ReadAsStreamAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Stream.Null;
}
}
Code update after the comments and answer:
// in my main app, I have this code.
// How can I get the completed task or any error return by the task here.
backup.DoBackupAsync().Wait();
public async Task<Stream> DoSaveAsync()
{
using (var client = GetHttpClient(BaseAddress, SnapshotPath, ApiKey))
{
try
{
Stream snapshot = await GetSnapshot(client, SnapshotPath);
if (snapshot != Stream.Null)
{
snapshot.Position = 0;
SaveSnapshot(snapshot);
}
return snapshot;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
}
As the method is async, the backup.DoSaveAsync() line only start a Task but doesn't wait for the result, so you may call Console.ReadLine (and possibly exit your program) before the task is completed. You should return Task instead of void - it's generally bad design to have a void async method, and younhave to await backup.DoSaveAsync() either via await (if you call from an async method), either via .Wait().
Also, in case of error in GetData, you don't return any error for DoSaveAsync - you may want to deal with this, in the current code, you would print "Failed to get snapshot" and then "Backup has done successfully in SQL database". Consider to not use Console.ReadLine in GetData and return a Task in DoSaveAsync indicating success
No need to put a thread.sleep here - you already await the result.

Mixing async-await with fire-and-forget approach

I'm writing a websocket server using .NET's HttpListener class.
Essentially, I've got a HandleListener() function which wait for clients to connect and yield each client to HandleClient(WebSocket client). So I currently have:
private async void HandleListener()
{
try
{
while (listener != null && listener.IsListening)
{
HttpListenerContext listenerContext = await listener.GetContextAsync();
WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
WebSocket webSocket = webSocketContext.WebSocket;
clients.Add(webSocket);
await HandleClient(webSocket);
}
}
catch (HttpListenerException) { } // Got here probably because StopWSServer() was called
}
private async Task HandleClient(WebSocket client) { ... }
Problem is, I can't seem to process more then one client. It looks like the execution of HandleListener() halts as long as the first client is connected.
I tried removing the await from the call to HandleClient(), but I get the "because this call is not awaited..." error. I can make HandleClient() a async void method, but this is not an event handler.
BTW, the reason that HandleClient() is async Task is because it's doing, all over in a loop until the listener is dead:
recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None);
From what I understand, a fire-and-forget approach is bad overall, and I can't seem to achieve it with async-await implementation. But HandleClient() is a fire-and-forget method, and I don't see any other way of achieving what I need.
EDIT: Added current implementation of HandleClient():
private async Task HandleClient(WebSocket client)
{
try
{
ArraySegment<byte> recievedBuffer = new ArraySegment<byte>(new byte[BUFFER_SIZE]);
while (listener != null && listener.IsListening && client.State == WebSocketState.Open)
{
WebSocketReceiveResult recieveResult;
using (var ms = new MemoryStream())
{
do
{
recieveResult = await client.ReceiveAsync(recievedBuffer, CancellationToken.None);
ms.Write(recievedBuffer.Array, recievedBuffer.Offset, recieveResult.Count);
}
while (!recieveResult.EndOfMessage);
switch (recieveResult.MessageType)
{
case WebSocketMessageType.Close:
RemoveClient(client, WebSocketCloseStatus.NormalClosure, string.Empty);
break;
case WebSocketMessageType.Binary:
RemoveClient(client, WebSocketCloseStatus.InvalidMessageType, "Cannot accept binary frame");
break;
case WebSocketMessageType.Text:
OnRecieve?.Invoke(client, System.Text.Encoding.UTF8.GetString(ms.ToArray()));
break;
}
}
}
}
catch (WebSocketException ex)
{
RemoveClient(client, WebSocketCloseStatus.InternalServerError, ex.Message);
}
}
To prevent compiler warning, use method like this:
public static class TaskExtensions {
public static void Forget(this Task task) {
}
}
then just do
HandleClient(webSocket).Forget()
If you go this route, ensure that you handle all exceptions inside HandleClient somehow (wrap whole thing into try-catch for example). There is nothing inherently "bad" in this approach in this particular case.
Alternative approach would be:
HandleClient(webSocket).ContinueWith(task => {
if (task.IsFaulted && task.Exception != null) {
// handle it here
}
});
awaiting HandleClient is not an option in this case, as you see yourself.
it will do like that because you wrote code for it, my mean to say you wrote method as below.
private async void HandleListener()
{
try
{
while (listener != null && listener.IsListening)
{
HttpListenerContext listenerContext = await listener.GetContextAsync();
WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
WebSocket webSocket = webSocketContext.WebSocket;
clients.Add(webSocket);
await HandleClient(webSocket);
}
}
catch (HttpListenerException) { } // Got here probably because StopWSServer() was called
}
In this method when it encounter await control will get return to orignal caller ,till you await part got completed and next call start after it.
Check below image this how await and async works
If you just want fire and forget than try like this
private void HandleListener()
{
try
{
while (listener != null && listener.IsListening)
{
HttpListenerContext listenerContext = await listener.GetContextAsync();
WebSocketContext webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol: null);
WebSocket webSocket = webSocketContext.WebSocket;
clients.Add(webSocket);
HandleClient(webSocket);
}
}
catch (HttpListenerException) { } // Got here probably because StopWSServer() was called
}
which means dont wait for completion of task

How to propagate an exception from ContinueWith to the calling context of an infinite loop task

I have an infinite loop in a task. Under certain circumstances, this task throws an exception and terminates. Consider the following code snippet.
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
int x = await FirstTask();
window.Title = "FirstTask completed with " + x.ToString();
}
catch (ArgumentException ex)
{
textbox.Text = ex.Message;
}
}
public async Task<int> FirstTask()
{
Task<int> secondTask;
int result;
secondTask = SecondTask();
textbox.Text = "Awaiting SecondTask result";
result = await secondTask;
textbox.Text = result;
secondTask.ContinueWith(async (Task t) =>
{
var thirdTask = ThirdTask();
thirdTask.ContinueWith(
async (m) =>
await Task.Run(() =>
{
throw thirdTask.Exception.InnerException;
}),
TaskContinuationOptions.OnlyOnFaulted);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
return 5;
}
public async Task<int> SecondTask()
{
await Task.Delay(1500);
return 8;
}
public async Task ThirdTask()
{
while (true)
{
await Task.Delay(500);
throw new ArgumentException("thirdException");
}
}
My problems lies in the inability to propagate the exception thrown from ThirdTask to the Button_Click event. Obviously, awaiting it is not an options, since it is an ongoing infinite operation (this is only simplified to fail quickly). I have, however, no problem with awaiting the "short" task which re-throws the exception, if it is only triggered once the ThirdTask fails. Note that I'm not interested in the doings of the ThirdTask unless it fails, that is while I'm able to await the FirstTask in the event handler.
Experimenting showed that even the most simple example doesn't propagate the exception from the ContinueWith block.
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
Task task = Task.Run(async () => { await Task.Delay(1000); });
task.ContinueWith( (t) => { throw new ArgumentException("test"); }, TaskContinuationOptions.OnlyOnRanToCompletion);
}
catch (ArgumentException ex)
{
textbox.Text = ex.Message;
}
}
So, how do I propagate an exception from ContinueWith to the calling context, given that the task that throws it has an infinite loop, which prevents me from awaiting it?
The problem I'm trying to solve is two-fold:
First, I need to initialize a resource (FirstTask), in order to do that, I first need to fetch it (SecondTask) and then to begin a process with it (ThirdTask), finally, the initialization of the resource (FirstTask) returns a value indicating the state of the resource, which doesn't depend on the process (ThirdTask). The process (ThirdTask) repeatedly invokes another task (in this case Task.Delay) and performs some work on it, but it can fail. In that case, it throws an exception which needs to be handled.
The second part is the general case of the second code example, of how to throw an exception from ContinueWith to be handled by the calling context.
given that the task that throws it has an infinite loop, which prevents me from awaiting it?
That in no way prevents you from awaiting it. The [easiest] way to handle the case that it throws an exception is specifically to await it.
You can simply implement the method as such:
public async Task FirstTask()
{
Task<int> secondTask = SecondTask();
textbox.Text = "Awaiting SecondTask result";
textbox.Text = await secondTask;
await ThirdTask();
}
If the click handler needs to both update a texbox with the results of the second operation and update the UI if the third fails, then you need to not wrap both of those operations in FirstTask and call them directly from the click handler:
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
textbox.Text = "Awaiting SecondTask result";
int x = await SecondTask();
window.Title = "SecondTask completed with " + x.ToString();
await ThirdTask();
}
catch (ArgumentException ex)
{
textbox.Text = ex.Message;
}
}

Categories

Resources