9

I read alot about async/await, but I still have some lack in understanding the following situation.

My question is, should I implement my "wrapper" methods as in DoSomething() or like in DoSomethingAsync().

So what's better (and why): Do I use await in wrapper methods or return the Task directly?

        public static async void Main()
        {
            await DoSomething();
            await DoSomethingAsync();
        }

        private static Task DoSomething()
        {
            return MyLibrary.DoSomethingAsync();
        }

        private static async Task DoSomethingAsync()
        {
            await MyLibrary.DoSomethingAsync().ConfigureAwait(false);
        }

        public class MyLibrary
        {
            public static async Task DoSomethingAsync()
            {
                // Here is some I/O
            }
        }
Jason Evans
  • 28,906
  • 14
  • 90
  • 154
coalmee
  • 1,334
  • 2
  • 16
  • 27
  • Try to avoid using `async main` - see http://stackoverflow.com/questions/9208921/async-on-main-method-of-console-app for more info. – rhughes Feb 16 '16 at 13:10
  • This answer could be helpful to you : http://stackoverflow.com/questions/22808475/how-to-force-execution-to-stop-till-asynchronous-function-is-fully-executed – user3340627 Feb 16 '16 at 13:31
  • See [Any difference between “await Task.Run(); return;” and “return Task.Run()”?](http://stackoverflow.com/q/21033150/1768303). – noseratio Feb 17 '16 at 05:36

3 Answers3

8

Async/Await are still relatively new, but there are some good practices to help clarify an API. The basics are this:

  • A method declaring itself as async means it is expecting to await later on
  • async implicitly creates a Task for you.
  • await is like a bookmark. The application resumes where the await keyword was used.
  • You cannot await anything that is not IAwaitable (most commonly a Task) (citation)

In an application where there are both asynchronous calls and synchronous calls, we've adopted a naming convention:

  • async calls return Task or Task<T> and append the word Async to the end of the name.
  • Synchronous calls (the default) simply work like any method does and there is no special convention.

Often, there can be two methods which do the same thing, but one is synchronous and the other not. You can either implement it two different ways, or you can wrap one with the other. It really depends on your needs and what is going to give you the more responsive application.

In the example above, the proper way to handle async and normal method calls would be for MyLibrary to expose two methods. The example would be like this:

 public static class MyLibrary
 {
     public static void DoSomething()
     {
         // do some work
     }

     public static async Task DoSomethingAsync()
     {
         // do similar work but use async API,
         // can also simply call DoSomething().
     }
 }

 // In another part of code would be called like this:
 public static async Task BiggerMethod()
 {
     MyLibrary.DoSomething();
     await MyLibrary.DoSomethingAsync();
 }

What you want to avoid is wrapping an async method with a regular method. As soon as you work with the Task directly, you lose all benefits of async and await and you introduce places where your code can deadlock.

Berin Loritsch
  • 11,400
  • 4
  • 30
  • 57
4

Berin's answer is good but does not explicitly address the particular case in your question, which is the following example:

1: Returning the Task directly

private static Task DoSomething()
{
    return MyLibrary.DoSomethingAsync();
}

2: Awaiting the Task and returning the result

private static async Task DoSomethingAsync()
{
    await MyLibrary.DoSomethingAsync().ConfigureAwait(false);
}

In this case, the only difference between returning the Task directly and awaiting the Task and returning the response is that - in the latter case - a state machine must be created by the framework to manage the starting, suspending and continuing of the method awaiting the Task. This incurs some performance overhead.

Generally speaking, if you can just return a Task and allow it to be awaited higher up, you should. In most (but certainly not all) real cases, however, this won't be possible as you will want to perform some processing of the result before returning (and this is exactly what async/await helps you to achieve).

Ant P
  • 24,820
  • 5
  • 68
  • 105
  • You should also note that `async` implicitly creates the `Task` for you, while a regular method needs to call `Task.StartNew()` if it wants to return a `Task` object itself. – Berin Loritsch Feb 16 '16 at 13:28
  • @BerinLoritsch only if it's the intention to actually create a new Task - in this case the Task is already supplied by `MyLibrary` and can simply be returned. – Ant P Feb 16 '16 at 13:33
  • Thanks for the answer. Shouldn't you use Task.FromResult() instead Task.StartNew() when returning as Task from regular method? – coalmee Feb 16 '16 at 14:18
  • @coalmee, `Task.FromResult(quicklyGetResult())` - because it just wraps already computed (sync or async) result, but `Task.Run(() => slowlyGetResult())` - to start computing result asyncronously. So the answer is `it depends`. – pkuderov Feb 16 '16 at 14:38
  • is'nt the 1st one block the caller thread? – Ehsan Sajjad Feb 16 '16 at 17:27
  • That's not the only difference: http://stackoverflow.com/questions/21033150/any-difference-between-await-task-run-return-and-return-task-run – noseratio Feb 17 '16 at 05:38
  • @Noseratio of course - using `await` rather than not using it at all will introduce subtleties and gotchas associated with async/await - I was focusing on the (lack of) behavioural difference between a successful `return await x` and `return x` as that's what the question seemed to call for. Probably should have qualified that though. – Ant P May 05 '16 at 07:40
0

There's no difference in case of one-liners. But in case of at least two-async-liners, e.g.:

public static async void Main()
{
    await DoSomething();
    await DoSomethingAsync();
}

private static Task DoSomethingTwice()
{
    var do1 = MyLibrary.DoSomethingAsync();

    // when you await DoSomethingTwice, next line's reached
    // when do1 task may not be completed
    var do2 = MyLibrary.DoSomethingAsync();

    // return what???
    // how to combine them?

    // okay, you can do that
    return Task.WhenAll(do1, do2)
}

private static async Task DoSomethingTwiceAsync()
{
    await MyLibrary.DoSomethingAsync().ConfigureAwait(false);

    // when you await DoSomethingTwiceAsync, next line's reached 
    // when prev-line task is completed
    await MyLibrary.DoSomethingAsync().ConfigureAwait(false);
}

public class MyLibrary
{
    public static async Task DoSomethingAsync()
    {
        // Here is some I/O
    }
}

Summary:

  • if you have to organize you method as a chain of sequential asyncronous peaces of work: doAsync1() -> doAsync2() -> doAsync3(),

i.e. every next peace needs complete result of previous one, then you should await every call as:

async Task DoAsync()
{
    await doAsync1();
    await doAsync2();
    await doAsync3();
}
  • but you cannot use await in non-async methods. So your cannot do it in your Task DoSomething() style, only as async Task DoSomethingAsync().
  • if there's no asyncronous chain - you can do whatever you want as in my first example above
pkuderov
  • 3,501
  • 2
  • 28
  • 46
  • Important to note that the above (`DoAsync`) is only appropriate if you need the tasks to be run sequentially - if you want them to be concurrent then you need to set them all off and *then* await them. – Ant P Feb 16 '16 at 13:35
  • @AntP Thanks! That's exactly what I meant as a `chain of asyncronous calls`. Maybe I should say `sequential asyncronous peaces of work` instead. And your second case is in my first example - method `DoSomethingTwice`. – pkuderov Feb 16 '16 at 13:38