Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sequential processing of asynchronous tasks

Assume the following synchronous code:

try {     Foo();     Bar();     Fubar();     Console.WriteLine("All done"); } catch(Exception e) // For illustration purposes only. Catch specific exceptions! {     Console.WriteLine(e); } 

Now assume all these methods have an Async counterpart and I have to use those for some reason, so simply wrapping the whole thing in a new task is not an option.
How would I achieve the same behavior?
What I mean with "same" is:

  1. Execute a handler for the exception, if one is thrown.
  2. Stop execution of the following methods, if an exception is thrown.

The only thing I was able to come up with is horrible:

var fooTask = FooAsync(); fooTask.ContinueWith(t => HandleError(t.Exception),                      TaskContinuationOptions.OnlyOnFaulted); fooTask.ContinueWith(     t =>     {         var barTask = BarAsync();         barTask.ContinueWith(t => HandleError(t.Exception),                              TaskContinuationOptions.OnlyOnFaulted);         barTask.ContinueWith(             t =>             {                 var fubarTask = FubarAsync();                 fubarTask.ContinueWith(t => HandleError(t.Exception),                                        TaskContinuationOptions.OnlyOnFaulted);                 fubarTask.ContinueWith(                     t => Console.WriteLine("All done"),                     TaskContinuationOptions.OnlyOnRanToCompletion);             },              TaskContinuationOptions.OnlyOnRanToCompletion);     },      TaskContinuationOptions.OnlyOnRanToCompletion); 

Please note:

  • I need a solution that works with .NET 4, so async/await is out of the question. However, if it would work with async/await feel free to show how.
  • I don't need to use the TPL. If it is impossible with the TPL another approach would be OK, maybe with Reactive Extensions?
like image 910
Daniel Hilgarth Avatar asked Jan 31 '13 16:01

Daniel Hilgarth


People also ask

What are asynchronous tasks?

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params , Progress and Result , and 4 steps, called onPreExecute , doInBackground , onProgressUpdate and onPostExecute .

What is task in asynchronous programming?

What Is A Task In C#? A task in C# is used to implement Task-based Asynchronous Programming and was introduced with the . NET Framework 4. The Task object is typically executed asynchronously on a thread pool thread rather than synchronously on the main thread of the application.

Can async method have multiple awaits?

For more information, I have an async / await intro on my blog. So additionally, if a method with multiple awaits is called by a caller, the responsibility for finishing every statement of that method is with the caller.

What is asynchronous method in C#?

C# has a language-level asynchronous programming model, which allows for easily writing asynchronous code without having to juggle callbacks or conform to a library that supports asynchrony. It follows what is known as the Task-based Asynchronous Pattern (TAP).


1 Answers

Here's how it would work with async:

try {     await FooAsync();     await BarAsync();     await FubarAsync();     Console.WriteLine("All done"); } catch(Exception e) // For illustration purposes only. Catch specific exceptions! {     Console.WriteLine(e); } 

This would work on .NET 4.0 if you installed the (prerelease) Microsoft.Bcl.Async package.


Since you're stuck on VS2010, you can use a variant of Stephen Toub's Then:

public static Task Then(this Task first, Func<Task> next) {   var tcs = new TaskCompletionSource<object>();   first.ContinueWith(_ =>   {     if (first.IsFaulted) tcs.TrySetException(first.Exception.InnerExceptions);     else if (first.IsCanceled) tcs.TrySetCanceled();     else     {       try       {         next().ContinueWith(t =>         {           if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions);           else if (t.IsCanceled) tcs.TrySetCanceled();           else tcs.TrySetResult(null);         }, TaskContinuationOptions.ExecuteSynchronously);       }       catch (Exception exc) { tcs.TrySetException(exc); }     }   }, TaskContinuationOptions.ExecuteSynchronously);   return tcs.Task;  } 

You can use it as such:

var task = FooAsync().Then(() => BarAsync()).Then(() => FubarAsync()); task.ContinueWith(t => {   if (t.IsFaulted || t.IsCanceled)   {     var e = t.Exception.InnerException;     // exception handling   }   else   {     Console.WriteLine("All done");   } }, TaskContinuationOptions.ExcecuteSynchronously); 

Using Rx, it would look like this (assuming you don't have the async methods already exposed as IObservable<Unit>):

FooAsync().ToObservable()     .SelectMany(_ => BarAsync().ToObservable())     .SelectMany(_ => FubarAsync().ToObservable())     .Subscribe(_ => { Console.WriteLine("All done"); },         e => { Console.WriteLine(e); }); 

I think. I'm not an Rx master, by any means. :)

like image 186
Stephen Cleary Avatar answered Sep 25 '22 13:09

Stephen Cleary