What is the recommended way to call generic callbacks from an async Task
? For example, progress, exception handling and completion (but could be other states).
The following code shows one way to implement it, but feels like there should be a cleaner way to handle this. (FWIW I've seen plenty of examples using ContinueWith
as a completion callback, but this doesn't really deal with other cases like progress and exception handling).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Testcode
{
class TestApp
{
public static void Main()
{
AsyncCallbackTest test = new AsyncCallbackTest();
test.RunTest();
}
}
interface ICallback
{
void onProgress(String status);
void onCompleted();
void onFaulted(Exception e);
}
class AsyncCallbackTest : ICallback
{
public void RunTest()
{
//run task in background
Task task = new Task(new Action<Object>(ExampleTask), this);
task.Start();
//do something else here
//...
//wait for task completion
task.Wait();
}
public void onProgress(string status)
{
Console.Out.WriteLine(status);
}
public void onCompleted()
{
Console.Out.WriteLine("COMPLETED");
}
public void onFaulted(Exception e)
{
Console.Out.WriteLine("EXCEPTION: " + e.Message);
}
private void ExampleTask(object ocb)
{
ICallback callback = ocb as ICallback;
//do some work
try
{
DoSomething(callback);
}
catch (Exception e)
{
//how to pass exceptions/errors?
callback.onFaulted(e);
}
}
//example long-running task
private void DoSomething(ICallback callback)
{
callback.onProgress("Starting");
Thread.Sleep(5000);
callback.onProgress("Step 1 complete");
Thread.Sleep(5000);
callback.onProgress("Step 2 complete");
Thread.Sleep(5000);
callback.onCompleted();
}
}
}
Completion and exception handling is very easy with async
-await
. Progress handling is exactly what the IProgress
interface is for.
So the signature of your method would be something like:
public Task DoSomething(IProgress<string> progress = null);
And you would call it like this:
try
{
await DoSomething(new Progress<string>(status => Console.WriteLine(status)));
Console.WriteLine("COMPLETED");
}
catch (Exception e)
{
Console.WriteLine("EXCEPTION: " + e.Message);
}
If you can't use C# 5.0, then you could use ContinueWith()
instead of await
and you would have to write your own IProgress
and Progress
:
DoSomething(new Progress<string>(status => Console.WriteLine(status)))
.ContinueWith(t =>
{
if (t.Exception != null)
Console.WriteLine("EXCEPTION: " + t.Exception.InnerException.Message);
else
Console.WriteLine("COMPLETED");
});
You could use the IProgress<T>
interface, or its Progress<T>
implementation. You can find a blog post describing its usage here, but it is basically like using callbacks.
Using the Reactive Extensions like @Kirill Shlenskiy suggested can be an option too, but I think adding support for that to an existing method using Tasks might not be the easiest solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With