Reputation: 3214
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();
}
}
}
Upvotes: 4
Views: 559
Reputation: 244848
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");
});
Upvotes: 4
Reputation: 10958
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.
Upvotes: 3