16

Summary: In a library method, when should I use the async and await keywords instead of returning a Task directly?

I believe my question is related to this one. However, that question is about .NET 4.0 and the TPL, while I'm using .NET 4.6 with the async and await keywords. So, I think my question might get different answers because these keywords didn't exist when the linked question was answered.

Explanation: I'm writing a simple wrapper for an external WCF service and the wrapper makes multiple SendAsync calls. Now I think that each wrapper method should just return a Task<> directly without being awaited. My understanding is that async/await should be used on the application layer, and not within a library.

So, for example, here is the approach that I think I should take for each wrapper method:

private Task<SignResponse> GetSignDataAsync(SigningRequestType request)
{
    return _service.SendAsync(request);
}

But on the Internet, I found several posts that use this approach instead:

private async Task<SignResponse> GetSignDataAsync(SigningRequestType request)
{
    return await _service.SendAsync(request).ConfigureAwait(false);
}

And here is another example that I found on technet:

async Task PutTaskDelay()
{
    await Task.Delay(5000);
} 

private async void btnTaskDelay_Click(object sender, EventArgs e)
{
    await PutTaskDelay();
    MessageBox.Show("I am back");
}

So, when should I use the second approach (the one that includes the async and await keywords)? Why not just return a whole Task without making PutTaskDelay async? I think that I should return Task directly whenever it is possible, and use async/await to get a final result in the application layer only. Am I right? If not, what is the difference between the two approaches that I show here?

My concern: When the async and await keywords are used, it seems that it just provides additional work to the compiler without any benefit.

Community
  • 1
  • 1
Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151
  • 1
    [Possible duplicate?](http://stackoverflow.com/questions/14455293/how-to-and-when-use-async-and-await) – Igor Mar 16 '16 at 12:40
  • You don't fully understand what the async keyword means. Whether or not you use it in your code, the caller would never know about it. Even the runtime it self has no notion of async / await. – shay__ Mar 16 '16 at 13:00
  • 1
    `async`/`await` are *implementation* details of your methods. It matters not one jot whether your method is declared `async Task Method()` or just `Task Method()`, so far as your *callers* are concerned. (In fact, you are free to change between these two at a later point in time without it being considered a breaking change) – Damien_The_Unbeliever Mar 16 '16 at 13:00
  • @Alex - I think your question is a good one and I did a fair amount of editing to hopefully make it even more useful to others and bring more attention to it. I think I faithfully preserved the intent of your question...if not, please correct any misunderstandings I may have. Also, in your code examples, I changed `SignAsync` to `SendAsync`. I hope that was what you intended. – DavidRR May 20 '16 at 14:39
  • 1
    @DavidRR thank you for editing. No, when i wrote `SignAsync` I meant `async Sign` method, because I called it to get a digital signature for byte array in some certification center. But I won't edit this post becuase method name `Sign` or `Send` doesn't matter. I consider that intent is actually preserved, so I just thank you for a good job :) – Alex Zhukovskiy May 21 '16 at 10:26
  • @Alex - In your original question, you stated: *"Explanation: I'm writing a simple wrapper for an external WCF service, so I have multiple `SendAsync` calls."* So, I was a bit confused when I saw `SignAsync` in your code examples. Also, after quickly checking MSDN, the versions of `SendAsync` and `SignAsync` that I looked at both take more than one parameter. So, if you think you can make the code examples more realistic, I encourage you to do so. That's because some people might arrive at your question looking for `SendAsync` or `SignAsync`. – DavidRR May 23 '16 at 12:36
  • @DavidRR I got it, `SendAsync` is fine, because as I said, specific method name doesn't matter in this case. – Alex Zhukovskiy May 23 '16 at 12:38
  • 1
    @Alex - I certainly agree that the use of `SendAsync` or `SignAsync` isn't central to your question. Our comments should clear up any confusion that someone might have if they arrive here looking for `SendAsync` or `SignAsync`. – DavidRR May 23 '16 at 12:47
  • Related: [At the end of an async method, should I return or await?](http://stackoverflow.com/q/17886992/1497596) and [What is the purpose of “return await” in C#?](http://stackoverflow.com/q/19098143/1497596). – DavidRR May 23 '16 at 17:16

2 Answers2

9

Should I use async await in library?

It all depends. If you're going to take advantage of the asynchronous programming paradigm, then the answer is "yes," the async and await keywords are needed most of the time. More than likely, you will find yourself needing to use async/await. That is because in most situations it would be difficult to use only Task and Task<T> as you will more than likely need to reason about the results of the async operations that you invoke.

Additionally, based on your question it seems as though you may have some confusion about the keywords themselves and how they relate to the Task and Task<T> types. Allow me to clarify this for you.

The async keyword allows a method to use the await keyword. The best practice is to have all async methods return either Task or Task<T> unless you are unable to (for example, a button click event handler as you demonstrated above).

Methods that return Task or Task<T> represent asynchronous operations. When you are in a library it is suggested to always use .ConfigureAwait(false) for reasons detailed here. Additionally, I always point people to this detailed article on the subject.

To differentiate the two approaches in your question:

The method below returns a Task<SignResponse>. This is an async operation that represents the work to sign in. The method can be awaited by the caller to get SignResponse.

private Task<SignResponse> GetSignDataAsync(SigningRequestType request)
{
    return _service.SignAsync(request);
}

Likewise, this version does the same thing...except that the async/await keywords are not needed. The reason they are not needed is that the method itself does not need to use SignResponse and therefore it could simply return Task<SignResponse> as shown above. And as you indicated in your question, there is indeed a penalty when you use the async/await keywords when they are not needed. Doing so adds an extra state machine step as the result is yielded, since its awaited.

private async Task<SignResponse> GetSignDataAsync(SigningRequestType request)
{
    return await _service.SignAsync(request).ConfigureAwait(false);
}

Finally, if you needed to reason about the response, you could use the aforementioned keywords to do so like this:

private async Task<SignResponse> GetSignDataAsync(SigningRequestType request)
{
    var result = await _service.SignAsync(request).ConfigureAwait(false);
    if (result.SomeProperty == SomethingWeCareToCheck)
    {
        _log.Log("Wow, this was un-expected...");
    }
    return result;
}
DavidRR
  • 18,291
  • 25
  • 109
  • 191
David Pine
  • 23,787
  • 10
  • 79
  • 107
  • 2
    Might be worth mentioning that there is a [feature request](https://github.com/dotnet/roslyn/issues/1981) for the compiler to be made smarter about `return await` (where that's the only await in the method). – Damien_The_Unbeliever Mar 16 '16 at 13:17
  • In a nutshell, I should use a direct task return whenever is possible, however, when I need a result for some reason or I want to run another task after this one ends, I should mark it as `async/await`. Of course I can write it manually with `ContinueWith`, but it's better to use built-in functionallity. Of course, we should not forget about `ConfigureAwait`. Some helpful links I readed before asking the question (just in case): [Zen of async](https://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-829T), [TPL Pattern](https://www.microsoft.com/en-us/download/confirmation.aspx?id=19957) – Alex Zhukovskiy Mar 16 '16 at 14:29
  • I also found that `await` should be also used when exception handling should be done here. I mean if you just return a Task (from WCF service for example) and it throws an exception, you cannot log it becuase it fails in internal WCF's `await`. But when you await it in service, you can use unhandled exception handler to log it. – Alex Zhukovskiy Jun 27 '16 at 12:35
-2

Don't take my word for it, because I never understood async/await that well, but what bothered me most about the idea was that all methods using async had to be marked as async as well, which annoyed me to no end.

I guess in a library giving people the option to choose how to use the methods is a good thing, so you should use async, however I always found it more clear to explicitly use the Tasks directly.

Siderite Zackwehdex
  • 6,293
  • 3
  • 30
  • 46
  • Actually, this is a bad idea. Either the method is synchronous or not. Using Tasks is no different from using `async/await`, these keywords are just sugar around *Tasks*. The caller will never now that a library function is marked `async` – Panagiotis Kanavos Mar 16 '16 at 13:04
  • unless you want to use await and the method is not marked async, therefore it won't work :) My advice was to mark the methods with async, as this will allow both scenarios: await or just Tasks, like I do it. – Siderite Zackwehdex Mar 16 '16 at 13:23
  • 1
    @SideriteZackwehdex - but the decision on whether to use `async`/`await` in any particular method is a *local* decision. It impacts neither code that calls yours nor code you call into. As I said in a comment to the question, it's an *implementation* detail, and you're free to add or remove them from any particular method. – Damien_The_Unbeliever Mar 16 '16 at 13:28
  • How can you say that when async marks the method you are calling and await is in the code you are writing? It seems to me that it impacts both caller and callee. The library needs to implement async methods in order to enable the use of await... or am I getting it wrong here? – Siderite Zackwehdex Mar 16 '16 at 13:33
  • 1
    No, for the caller, all that matters is whether your method returns `SomeType` or `Task`. That decision (return type) is completely separate from `async`/`await`. It is true that if you take a mostly synchronous code base and want to make it asynchronous, you will frequently be *both* changing method return types and decorating functions with `async`/`await`, but be *very* clear that those are different actions. – Damien_The_Unbeliever Mar 16 '16 at 14:08