2

Say I am implementing an interface with the method signature

String FooBar();

And later on I am implementing this interface in my class

public String FooBar()
{
    return "";
}

But at some point I would like to await something

public async Task<String> FooBar()
{
    await Task.Delay(500);
    return "";
}

How can I await something in this scenario without having to alter the method signature to

Task<String> FooBar();

Is the proper way to do this with Task.WaitAll? Or is there a more accepted pattern?

EDIT: I do not feel that my question is an exact duplicate, though very much related, to How would I run an async Task<T> method synchronously? . I am more worried about how to implement the interface than I am about how to call the async function. But I do admit the bolded question does equate to the duplicate question.

I do see now it is more appropriate to return Task because of the possibility of implementors of the interface calling async methods. Otherwise I "lock them out" of doing so properly.

Community
  • 1
  • 1
KDecker
  • 6,928
  • 8
  • 40
  • 81
  • 2
    `task.GetAwaiter().GetResult()` is generally nicer than Wait or WaitAll since it will throw the actual exception wtihout the AggregateException wrapper. – Mike Zboray Aug 11 '16 at 17:12
  • 1
    Possible duplicate of [How would I run an async Task method synchronously?](http://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously) – Gerardo Grignoli Aug 11 '16 at 17:41

3 Answers3

5

How can I await something in this scenario without having to alter the method signature

The proper solution is to alter the method signature. Any interface method that could be implemented asynchronously should return Task/Task<T>.

(From a design perspective, this is very similar to deciding whether an interface should derive from IDisposable - the interface has to predict its own implementation details).

As far as improper solutions go, this is just another variant of the "call async method from sync method" question. There is no ideal, universal, or commonly-accepted solution. There are only a few hacks you can do, each with different drawbacks. I cover these in my async brownfield article.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
1

I write all my code where I can take advantage of async calls (and when it makes sense to do so) using async code. If there is code that needs to call the same method but synchronously then I prefer to write extension methods for my (interface) types where I need to expose a synchronous version of an async method. This allows me to not have to rewrite the business logic but instead it just calls through the existing implementation. It also means I have to write 1 less unit test later against the business logic. The only reason you might not want to do this in all scenarios is if there is a large loop that is performance critical, in that case the extra overhead of the async wrapper could be a problem and you should then invest in re-writing a 2nd version of that method.

// note that I added cancellation support, this is personal preference

public interface IFoo
{
    Task<String> FooBarAsync(CancellationToken token);
}

public class Foo : IFoo
{
    public async Task<String> FooBarAsync(CancellationToken token)
    {
        await Task.Delay(500, token);
        return "";
    }
}

public static class FooSyncExtender
{
    public static String FooBar(this IFoo foo)
    {
        // this is one way to call async from sync code
        return foo.FooBarAsync(CancellationToken.None).GetAwaiter().GetResult();
    }
}
Igor
  • 60,821
  • 10
  • 100
  • 175
0

If you leave method signature unchanged, it means method will no longer take benefits of async/await, which is use limited number of threads to do lots of async operations using auto-generated state machine by compiler. Once you use something like .Wait / .WaitAll / .GetResult inside your method it means you block current thread until async operation finishes.

Normally you want to have all your application async/await compatible all way down to get real benefits from it.

I would make interface with 2 methods: synchronous version and async version. Internally synchronous version may call async version and just wait for its execution.

dlxeon
  • 1,932
  • 1
  • 11
  • 14