kevmo314
kevmo314

Reputation: 4317

Catching an exception thrown in an asynchronous callback

I have a method that takes a callback argument to execute asynchronously, but the catch block doesn't seem to be catching any exceptions thrown by the synchronous call (this.Submit refers to a synchronous method).

public void Submit(FileInfo file, AnswerHandler callback)
{
    SubmitFileDelegate submitDelegate = new SubmitFileDelegate(this.Submit);
    submitDelegate.BeginInvoke(file, (IAsyncResult ar) =>
    {
        string result = submitDelegate.EndInvoke(ar);
        callback(result);
    }, null);
}

Is there a way to catch the exception thrown by the new thread and send it to the original thread? Also, is this the "proper" way to handle async exceptions? I wrote my code so it could be called like this (assuming the exception issue is fixed):

try
{
    target.Submit(file, (response) =>
    {
        // do stuff
    });
}
catch (Exception ex)
{
    // catch stuff
}

but is there a more proper or elegant way to do this?

Upvotes: 9

Views: 17117

Answers (3)

Adi Lester
Adi Lester

Reputation: 25201

If you're targeting .NET 4.0, you can utilize the new Task Parallel Library, and observe the Task object's Exception property.

public Task Submit(FileInfo file)
{
    return Task.Factory.StartNew(() => DoSomething(file));
}

private void DoSomething(FileInfo file)
{
    throw new Exception();
}

Then use it like this:

Submit(myFileInfo).ContinueWith(task =>
{
    // Check task.Exception for any exceptions.

    // Do stuff with task.Result
});

where DoSomething is the method you'd like call asynchronously, and the delegate you pass to ContinueWith is your callback.

More information about exception handling in TPL can be found here: http://msdn.microsoft.com/en-us/library/dd997415.aspx

Upvotes: 9

Cristian Lupascu
Cristian Lupascu

Reputation: 40516

This is not a 'best practice' solution, but I think it's a simple one that should work.

Instead of having the delegate defined as

private delegate string SubmitFileDelegate(FileInfo file);

define it as

private delegate SubmitFileResult SubmitFileDelegate(FileInfo file);

and define the SubmitFileResult as follows:

public class SubmitFileResult
{
    public string Result;
    public Exception Exception;
}

Then, the method that actually does the file submission (not shown in the question) should be defined like this:

private static SubmitFileResult Submit(FileInfo file)
{
    try
    {
        var submissionResult = ComplexSubmitFileMethod();

        return new SubmitFileResult { Result = submissionResult };
    }
    catch (Exception ex)
    {
        return new SubmitFileResult {Exception = ex, Result = "ERROR"};
    }
}

This way, you'll examine the result object, see if it has the Result or the Exception field set, and act accordingly.

Upvotes: 8

Mark Avenius
Mark Avenius

Reputation: 13947

In short, no.

When you call submitDelegate.BeginInvoke, it spawns the new thread, returns, and promptly exits your try/catch block (while the new thread runs in the background).

You could, however, catch all unhandled exceptions like this:

AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(YourException);

This will catch everything in the application domain, however (not just your async method).

Upvotes: 5

Related Questions