Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Throwing immediately from async method

The normal behavior for exceptions thrown from async Task methods is to stay dormant until they get observed later, or until the task gets garbage-collected.

I can think of cases where I may want to throw immediately. Here is an example:

public static async Task TestExAsync(string filename)
{
    // the file is missing, but it may be there again
    // when the exception gets observed 5 seconds later,
    // hard to debug

    if (!System.IO.File.Exists(filename))
        throw new System.IO.FileNotFoundException(filename);

    await Task.Delay(1000);
}

public static void Main()
{
    var task = TestExAsync("filename");
    try
    {
        Thread.Sleep(5000); // do other work
        task.Wait(); // wait and observe
    }
    catch (AggregateException ex)
    {
        Console.WriteLine(new { ex.InnerException.Message, task.IsCanceled });
    }
    Console.ReadLine();
}

I could use async void to get around this, which throws immediately:

// disable the "use await" warning
#pragma warning disable 1998
public static async void ThrowNow(Exception ex)
{
    throw ex;
}
#pragma warning restore 1998

public static async Task TestExAsync(string filename)
{
    if (!System.IO.File.Exists(filename))
        ThrowNow(new System.IO.FileNotFoundException(filename));

    await Task.Delay(1000);
}

Now I can handle this exception right on the spot with Dispatcher.UnhandledException or AppDomain.CurrentDomain.UnhandledException, at least to bring it to the user attention immediately.

Is there any other options for this scenario? Is it perhaps a contrived problem?

like image 821
noseratio Avatar asked Mar 29 '14 00:03

noseratio


1 Answers

If you really want to do this, you can use the same approach Jon Skeet used in his reimplementation of LINQ: create a synchronous method that can throw or call the real asynchronous method:

public static Task TestExAsync(string filename)
{
    if (!System.IO.File.Exists(filename))
        throw new System.IO.FileNotFoundException(filename);

    return TestExAsyncImpl(filename);
}

private static async Task TestExAsyncImpl(string filename)
{
    await Task.Delay(1000);
}

Keep in mind that I think that it's normal to assume that a Task returning method doesn't throw directly. For example, you can use Task.WhenAll() to get all exceptions from several operations under normal circumstances, but this approach won't work when the exception is thrown immediately.

like image 194
svick Avatar answered Oct 07 '22 07:10

svick