How to properly catch exceptions in multiple async method calls? - c#

I have some async method:
public async Task<Object> MethodAsync()
{
return await MakeHttpRequestAsync();
}
I need to make multiple calls of this method. One of them can throw an exception. But I need to keep all possible returned Objects from other method calls that succeeded.
Option 1:
Task t1 = MethodAsync();
Task t2 = MethodAsync();
try
{
Task.WaitAll(new Task[]{t1,t2});
}
catch(Exception e)
{
...
}
//are t1 and t2 finished execution here with success or exception?
Option 2:
Task t1 = MethodAsync();
Task t2 = MethodAsync();
try
{
t1.Wait()
}
catch(Exception e)
{
...
}
try
{
t2.Wait()
}
catch(Exception e)
{
...
}
//what happens if t2 throws exception before t1 finished?
Any other options?
edit:
Thought about third option:
public async Task<Tuple<Object, Object>> MultipleAsyncCallsAsync(some arguments)
{
Object result1 = null, result2 = null;
try
{
result1 = await MethodAsync();
}
catch(Exception e)
{
}
try
{
result2 = await MethodAsync();
}
catch (Exception e)
{
}
return Tuple.Create(result1, result2);
}
edit #2:
Looks like third option is not working, but it's working this way:
public async Task<Tuple<object, object>> MultipleAsyncCallsAsync(some arguments)
{
object result1 = null, result2 = null;
Task<object> t1 = null, t2 = null;
t1 = Task.Run(()=> MethodAsync());
t2 = Task.Run(()=> MethodAsync());
try
{
result1 = await t1;
}
catch(Exception e)
{
}
try
{
result2 = await t2;
}
catch (Exception e)
{
}
return Tuple.Create(result1, result2);
}

Put the tasks in an array and inspect their status like this:
var t1 = MethodAsync();
var t2 = MethodAsync();
var tasks = new[] {t1, t2};
try
{
await Task.WhenAll(tasks);
}
catch
{
var failedTasks = tasks.Where(t => t.IsFaulted);
var allExceptions = failedTasks.Select(t => t.Exception?.Flatten());
}
var results = tasks.Where(t => t.IsCompletedSuccessfully).Select(t => t.Result);
What is does is it awaits all tasks and then uses the task status (using IsCompletedSuccessfully ect.) to get the results of all the tasks that did complete succesfully.
You can determine the failed tasks using IsFaulted and get their exception using the tasks Exception property.
If you want to catch/inspect the exception before the call to Task.WhenAll(..) is completed wrap the code inside MethodAsync in a try/catch:
public async Task<Object> MethodAsync()
{
try
{
..
}
catch(Exception ex)
{
//handle or throw
return null;
}
}
Or, if you cannot modify MethodAsync for some reason, you can wrap the method in a try/catch using this:
Func<Task<Object>, Task<Object>> TryExecute = new Func<Task<Object>, Task<object>>(async t =>
{
try
{
return await t;
}
catch (Exception ex)
{
//handle exception
return null;
}
});
and use it like this:
var t1 = TryExecute(MethodAsync());
var t2 = TryExecute(MethodAsyncF());
var tasks = new[] {t1, t2};
await Task.WhenAll(tasks);

Whenever there's a question about this kind of procedure ("call n methods, (a)wait for them all to complete, and then retrieve the results for each method and do something with them"), a better solution is almost always to create a higher-order method.
For example, if you want to do some kind of logging or something but otherwise ignore exceptions, then you can create a higher-order method like this:
public async Task<T> LogAndIgnoreExceptions(Task<T> task) where T : class
{
try
{
return await task;
}
catch (Exception ex)
{
Log(ex);
return null;
}
}
Then you can use Task.WhenAll in a very natural way:
var results = await Task.WhenAll(
LogAndIgnoreExceptions(MethodAsync()),
LogAndIgnoreExceptions(MethodAsync())
);
if (results[0] is not null)
{
T result = results[0];
}
else
{
// First call had an error.
}
Or, if you want to preserve the exception details and results, what you want is a little helper type like Try to enable railway programming:
var results = await Task.WhenAll(
Try.Create(MethodAsync()),
Try.Create(MethodAsync())
);
if (results[0].IsValue)
{
T result = results[0].Value;
}
else
{
Exception exception = results[0].Exception;
}

Related

What is the best approach/practice for awaiting multiple independent tasks?

var contractSchemaTask = contractSchemaRepository.GetByContractIdAsync(data.Id);
var sectionsTask = sectionRepository.GetAllByContractIdAsync(id);
var latestContractIdTask = contractRepository
.GetLatestContractIdByFolderIdAsync(data.FolderId.Value);
List<Task> allTasks = new List<Task>()
{ contractSchemaTask, sectionsTask, latestContractIdTask };
while (allTasks.Any())
{
Task finished = await Task.WhenAny(allTasks);
if (finished == contractSchemaTask)
{
var contractSchema = await contractSchemaTask;
result.ReturnData.IsSchedules = contractSchema.Count > 0 ? true : false;
}
else if (finished == sectionsTask)
{
List<Section> sections = await sectionsTask;
List<TextValueVM> SectionTabList = sections.Count > 0 ? sections
.OrderBy(a => a.SectionNumber)
.Select(a => new TextValueVM()
{ Text = a.ToString(), Value = a.Id.ToString() })
.ToList() : new List<TextValueVM>();
bool IsSectionsLinked = false;
int linkSectionCount = sections
.Where(x => x.LinkSectionId != null && x.LinkSectionId != Guid.Empty)
.ToList()
.Count();
if (linkSectionCount == 0 && sections.Count > 0)
{
List<Guid> sectionIds = sections.Select(x => x.Id.Value).ToList();
List<Section> currentContractLinkSections = await sectionRepository
.GetSectionsByLinkSectionIdAsync(sectionIds);
if (currentContractLinkSections.Count > 0)
{
IsSectionsLinked = true;
}
}
else if (linkSectionCount > 0)
{
IsSectionsLinked = true;
}
result.ReturnData.SectionTabList = SectionTabList;
result.ReturnData.IsSectionsLinked = IsSectionsLinked;
}
else if (finished == latestContractIdTask)
{
Guid LatestContractId = await latestContractIdTask;
result.ReturnData.isLatestContract
= (data.Id == LatestContractId) ? true : false;
}
allTasks.Remove(finished);
}
I am working on a asp.net core 3.0 WebAPI project. Above is the sample code for independent tasks that I handle using a while loop. Is there any better or efficient approach for handling independent tasks in the asynchronous programming?
P.S: All the 3 tasks are independent and may vary on their response time depending upon the number of records fetched from the database.
You should do it like this:
public Task Main()
{
var result = new Result();
return Task.WhenAll(TaskOne(result), TaskTwo(result), TaskThree(result));
}
private async Task TaskOne(Result result)
{
var contractSchema = await contractSchemaRepository.GetByContractIdAsync(data.Id);
//your logic for task1, set related result properties
}
private async Task TaskTwo(Result result)
{
var sections = await sectionRepository.GetAllByContractIdAsync(id);
//your logic for task2, set related result properties
}
private async Task TaskThree(Result result)
{
var latestContractId = await contractRepository.GetLatestContractIdByFolderIdAsync(data.FolderId.Value);
//your logic for Task3, set related result properties
}
Result class should be implemented as thread-safe because tasks can be executed simultaneously. If you just set different properties in each method it should be OK.
Combining Task.WhenAll with Continue allows you to execute code as soon the task finish without having to await the rest of the tasks.
class Test {
public static async Task Main() {
var t1 = AsyncWork1().ContinueWith((t) => Console.WriteLine($"Task1 finished with value {t.Result}"));
var t2 = AsyncWork2().ContinueWith((t) => Console.WriteLine($"Task2 finished with value {t.Result}"));
var t3 = AsyncWork3().ContinueWith((t) => Console.WriteLine($"Task3 finished with value {t.Result}"));
await Task.WhenAll(new[] { t1, t2, t3 });
//here we know that all tasks has been finished and its result behaviour executed.
Console.ReadKey();
}//main
public static async Task<int> AsyncWork1() {
await Task.Delay(1000);
return 1;
}
public static async Task<string> AsyncWork2() {
await Task.Delay(100);
return "work2";
}
public static async Task<bool> AsyncWork3() {
await Task.Delay(500);
return true;
}
}//class Test
Compare with this:
class Test {
public static async Task Main() {
var t1 = AsyncWork1();
var t2 = AsyncWork2();
var t3 = AsyncWork3();
await Task.WhenAll(new[] { t1, t2, t3 });
//all task finished but now we have to execute the result behaviour in a sync way
Console.WriteLine($"Task1 finished with value {t1.Result}");
Console.WriteLine($"Task2 finished with value {t2.Result}");
Console.WriteLine($"Task3 finished with value {t3.Result}");
Console.ReadKey();
}//main
public static async Task<int> AsyncWork1() {
await Task.Delay(1000);
return 1;
}
public static async Task<string> AsyncWork2() {
await Task.Delay(100);
return "work2";
}
public static async Task<bool> AsyncWork3() {
await Task.Delay(500);
return true;
}
}//class Test

Exception not fired when using TPL

I have the following code which does not fire the AggregateException
Aggregate Exception is not fired and I don't understand why? Normally it should as Aggregate exception is used to catch exceptions while running code using tasks
class Program
{
static void Main(string[] args)
{
var task1 = Task.Factory.StartNew(() =>
{
Test();
}).ContinueWith((previousTask) =>
{
Test2();
});
try
{
task1.Wait();
}
catch (AggregateException ae)
{
foreach (var e in ae.InnerExceptions)
{
// Handle the custom exception.
if (e is CustomException)
{
Console.WriteLine(e.Message);
}
// Rethrow any other exception.
else
{
throw;
}
}
}
}
static void Test()
{
throw new CustomException("This exception is expected!");
}
static void Test2()
{
Console.WriteLine("Test2");
}
}
public class CustomException : Exception
{
public CustomException(String message) : base(message)
{ }
}
}
That's because you are waiting for completion of your continuation task (which runs Test2()), not for completion of task which runs Test(). First task fails with exception and then continuation task does nothing with this exception (you don't check if previousTask has failed) and completes successfully. To catch that exception you need to wait for first task or check result of it in continuation:
var task1 = Task.Factory.StartNew(() =>
{
Test();
});
var task2 = task1.ContinueWith((previousTask) =>
{
Test2();
});
or
var task1 = Task.Factory.StartNew(() =>
{
Test();
}).ContinueWith((previousTask) =>
{
if (previousTask.Exception != null) {
// do something with it
throw previousTask.Exception.GetBaseException();
}
Test2();
}); // note that task1 here is `ContinueWith` task, not first task
That's all not related of course to whether you should really do it like this or not, just to answer the question.

Async await call does not return

I have a problem, where i call an async method, and the call does not return back. I assume its a race of threads. How do I write this correctly?
This is where it starts. I first call an async method called "GetCachedValuesAsync"
public void OnNavigatingTo(NavigationParameters parameters)
{
Task.Run(async () =>
{
await GetCachedValuesAsync();
ClipRefernce = GenerateRefernce(clips);
});
}
Here is the method signature for GetCachedValueAsync
public async Task GetCachedValuesAsync()
{
try
{
clips = await BlobCache.LocalMachine.GetObject<List<Clip>>("clips");
}
catch (KeyNotFoundException ex)
{
clips = new List<Clip>();
}
}
I do not get the call returned from BlobCache, BlobCahce method is part of a library called akavache.
The code also does not hit: ClipRefernce = GenerateRefernce(clips);
I appreciate your help
Edit 1
This is GenerateRefernce method.
public string GenerateRefernce(List<Clip> clips)
{
string refernce = "";
if(clips.Count > 0)
{
var clip = clips.LastOrDefault();
refernce = String.Format("Ref {0:yyyy}/{1}",DateTime.Now , clip.ClipId + 1);
}
else{
refernce = String.Format("Ref {0:yyyy}/{1}", DateTime.Now, 1);
}
return refernce;
}
You need to remove the sync method from the Task.Run like this:
public void OnNavigatingTo(NavigationParameters parameters)
{
Task.Run(async () =>
{
await GetCachedValuesAsync();
});
ClipRefernce = GenerateRefernce(clips);
}
public async Task GetCachedValuesAsync()
{
try
{
clips = await BlobCache.LocalMachine.GetObject<List<Clip>>("clips");
}
catch (KeyNotFoundException ex)
{
clips = new List<Clip>();
}
}

Xamarin.Forms - BeginInvokeOnMainThread for an async Action

I am familiar with the rules about updating UI elements on the UI thread using the Device.BeginInvokeOnMainThread, however I have an operation that needs to be run on the UI thread that is actually a Task.
For example, the Push/PopAsync methods on XLabs.Forms.Mvvm seem to behave incorrectly on iOS unless they are invoked on the UI thread. There is also another example in the Acr.UserDialogs library for displaying toasts etc.
I know that making an Action async is basically creating an async void lambda and runs the risk of creating a deadlock in the case of an exception, obviously I don't want this to happen.
Does anybody have a workaround for performing async operations on the UI thread that doesn't involve marking the Action as async?
Since Xamarin.Forms 4.2 there is now a helper method to run tasks on the main thread and await them.
await Device.InvokeOnMainThreadAsync(SomeAsyncMethod);
Several overloads exist that should cover most scenarios:
System.Threading.Tasks.Task InvokeOnMainThreadAsync (System.Action action);
System.Threading.Tasks.Task InvokeOnMainThreadAsync (System.Func<System.Threading.Tasks.Task> funcTask);
System.Threading.Tasks.Task<T> InvokeOnMainThreadAsync<T> (System.Func<System.Threading.Tasks.Task<T>> funcTask);
System.Threading.Tasks.Task<T> InvokeOnMainThreadAsync<T> (System.Func<T> func);
Related PR: https://github.com/xamarin/Xamarin.Forms/pull/5028
NOTE: the PR had some bug that was fixed in v4.2, so don't use this in v4.1.
Just make sure you handle exceptions in your Action and you should be fine. The problem you described occurs when you don't handle the exceptions. Below is a very simple example of running an async method from the main thread.
private void Test()
{
Device.BeginInvokeOnMainThread(SomeMethod);
}
private async void SomeMethod()
{
try
{
await SomeAsyncMethod();
}
catch (Exception e) // handle whatever exceptions you expect
{
//Handle exceptions
}
}
private async Task SomeAsyncMethod()
{
await Navigation.PushModalAsync(new ContentPage());
}
Dropping this here in case someone wants to await the end of an action which has to be executed on the main thread
public static class DeviceHelper
{
public static Task RunOnMainThreadAsync(Action action)
{
var tcs = new TaskCompletionSource<object>();
Device.BeginInvokeOnMainThread(
() =>
{
try
{
action();
tcs.SetResult(null);
}
catch (Exception e)
{
tcs.SetException(e);
}
});
return tcs.Task;
}
public static Task RunOnMainThreadAsync(Task action)
{
var tcs = new TaskCompletionSource<object>();
Device.BeginInvokeOnMainThread(
async () =>
{
try
{
await action;
tcs.SetResult(null);
}
catch (Exception e)
{
tcs.SetException(e);
}
});
return tcs.Task;
}
}
Evaluating the Maui framework and the related WeatherTwentyOne demo. The dialogs were throwing exceptions on Android 29 related to auth modifications. Hat tip to this post Why does my app crash after alert dialog buttons are clicked
Anyway, I could not locate the aforementioned async helpers. So I adapted #Dbl's post to include awaitable generic results (or not)
using System;
using System.Threading.Tasks;
using Microsoft.Maui.Controls;
// ReSharper disable AsyncVoidLambda
namespace WeatherTwentyOne.Utils
{
public static class DeviceHelper
{
// https://stackoverflow.com/a/47941859/241296
public static Task<T> RunOnMainThreadAsync<T>(Func<Task<T>> op)
{
var tcs = new TaskCompletionSource<T>();
Device.BeginInvokeOnMainThread(async
() => {
try
{
var t = await op();
tcs.SetResult(t);
}
catch (Exception e)
{
tcs.SetException(e);
}
});
return tcs.Task;
}
public static Task RunOnMainThreadAsync(Func<Task> op)
{
var tcs = new TaskCompletionSource();
Device.BeginInvokeOnMainThread(async
() => {
try
{
await op();
tcs.SetResult();
}
catch (Exception e)
{
tcs.SetException(e);
}
});
return tcs.Task;
}
}
}
Example usage:
var password = await Utils.DeviceHelper.RunOnMainThreadAsync<string>(async () => await DisplayPromptAsync("Login", "Enter password"));
Edit: 2022-03-26
In response to the following warning:
   BeginInvokeOnMainThread(Action) is obsolete: Use BindableObject.Dispatcher.Dispatch() instead
implementation revised to:
public static class DeviceHelper
{
public static Task<T> RunOnMainThreadAsync<T>(Func<Task<T>> op)
{
var tcs = new TaskCompletionSource<T>();
Application.Current?.Dispatcher.Dispatch(async () =>
{
try
{
var t = await op();
tcs.SetResult(t);
}
catch (Exception e)
{
tcs.SetException(e);
}
});
return tcs.Task;
}
public static Task RunOnMainThreadAsync(Func<Task> op)
{
var tcs = new TaskCompletionSource();
Application.Current?.Dispatcher.Dispatch(async () =>
{
try
{
await op();
tcs.SetResult();
}
catch (Exception e)
{
tcs.SetException(e);
}
});
return tcs.Task;
}
}

Passing exception from Task.Run to await properly and Debugger

This is the task, where the exception is being thrown.
public Task<SensorReading> ReadSensorsAsync(int cell)
{
return Task.Run(() => {
throw new ArgumentNullException();
...
This is the async method:
private async void TimerCallback(object state)
{
try
{
var tasksRead = Enumerable.Range(3, 35).Select(i => sensorReader.ReadSensorsAsync(i)).ToList();
await Task.WhenAll(tasksRead);
var r = tasksRead.Select(x => x.Result).ToList();
var taskRecordREsult = RecordReadingAsync(r).Result;
}
catch(Exception e)
{
// expecting to catch everything here
}
}
What I was expecting is that exception would be handled in await part of the code. However, I am getting unhandled exception in Visual Studio debugger.
Question - how to properly pass all kind of exception from Task.Run to awaitable part?
One possible solution is to extract your Task.Run lambda into a standalone method, and add the [DebuggerStepThrough] attribute to it. In theory (I haven't tested this), this will suppress the first exception in the debugger, and allow it to be caught later. It's not pretty, but it should do the trick.
try this program and see if you still get breaking..
its working for me
class Program
{
static void Main(string[] args)
{
new Program().TimerCallback();
}
public Task<string> ReadSensorsAsync(string cell)
{
return Task.Run(() =>
{
if(cell == null) throw new ArgumentNullException();
return cell;
});
}
public Task<string> RecordReadingAsync(IEnumerable<string> cell)
{
return Task.Run(() =>
{
return string.Join(",", cell);
});
}
public async void TimerCallback()
{
try
{
var tasksRead = new string[] { "1", null, "3" }.Select(s => ReadSensorsAsync(s));
var taskRecordResult = await RecordReadingAsync(await Task.WhenAll(tasksRead));
Debugger.Log(1, "test", taskRecordResult);
}
catch (Exception e)
{
//catches here
Debugger.Log(1, "test", e.Message ?? e.ToString());
}
}
}

Categories