9

Only the last line of the method below is using 'await', just before the method returns, so doesn't this mean that method is basically synchronous and should just be called "Get()" without the async modifier and the suffix Async?

public virtual async Task<TEntity> GetAsync(Guid id)
{
    // some more code here
    return await _dbSet.FindAsync(id);
}
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
epitka
  • 17,275
  • 20
  • 88
  • 141
  • 5
    [Optimize an async method that ends "return await e" to be non-async "return e" #1981](https://github.com/dotnet/roslyn/issues/1981) discusses some of the subtleties (particularly with respect to exceptions). In *most* circumstances I'd describe this as an anti-pattern if it's the only `await`. – Damien_The_Unbeliever Jan 25 '17 at 15:35
  • @Damien_The_Unbeliever Make that an answer, it would be highly benefical. – This company is turning evil. Jan 25 '17 at 15:37

4 Answers4

15

doesn't this mean that method is basically synchronous

No. It's asynchronous. You're probably thinking of sequential (progressing from one thing to the next), not synchronous (blocking the current thread). An await will pause the method (sequentially) but not block the thread (asynchronously). For more information, see my async intro.

without the async modifier

While you could elide the async/await keywords, I would recommend that you do not. This is because // some more code here may throw an exception. I cover this and other considerations in my blog post on eliding async and await.

and the suffix Async?

No, that suffix is appropriate for any method that returns an awaitable (e.g., Task). So, even if you elide the async and await, it's still returning a task that should be awaited, so it should still have the Async suffix.

You can think of it this way: the Async suffix is part of the API interface. The async keyword is an implementation detail. They often go together, but not always.

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

Its a matter of opinion. Generally the naming convention is anything that returns Task or Task<T> has a suffix of Async.

Your example above though would be better written like this so you do not have the extra overhead of the async wrapper. This is because you do not need to await the result in that method. The consumer of the method would then await the result if it used it directly.

public virtual Task<TEntity> GetAsync(Guid id, params Expression<Func<TEntity, object>>[] includeProperties))
{
    // some more code here
    return _dbSet.FindAsync(id);
}
Igor
  • 60,821
  • 10
  • 100
  • 175
  • But even if it returns a `Task` or `Task`, that doesn't mean it's necessarily async, right? I've always struggled with this question. – rory.ap Jan 25 '17 at 15:37
  • ...i.e. if you await it? What about "true" async operations (like disk reads, that are async at a deep down kernel level)? Hasn't the ship already sailed on them being async, i.e you can't make them *not* async if you call the async flavor of the method (e.g. `ReadLineAsync`)? – rory.ap Jan 25 '17 at 15:41
  • 2
    No, returning a `Task` represents a unit of work that *has already started*. (Unless you're mad and are returning cold tasks, but nobody should be doing that and they definitely won't be if they're leveraging `async`) – Damien_The_Unbeliever Jan 25 '17 at 15:42
  • @Damien_The_Unbeliever ^--- yes! – Igor Jan 25 '17 at 15:42
  • @Damien_The_Unbeliever -- but if it's already started, and the method that returned it was NOT called with `async`, then does it not return that task until the task has completed? – rory.ap Jan 25 '17 at 15:44
0

await doesn't block. It awaits asynchronously for an operation to complete. That means that the original thread is released while awaiting. In a desktop application, this means that the UI thread is released while waiting eg for an HTTP or database call to finish.

When that operation completes, execution is returned to the original thread (for desktop applications).

As it is, you can simplify your code by removing the async and await keywords and returning the Task immediatelly. The code doesn't do anything with the results of the operation, so it doesn't need to await for it to finish. This will be done by the caller of the method:

public virtual Task<TEntity> GetAsync(Guid id)
{
    // some more code here
    return _dbSet.FindAsync(id);
}

The real asynchronous operation in this code is FindAsync. The async keyword is just syntactic sugar that allows you to use the await keyword. If you don't need to await, you don't need the async keyword either

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
-1

There are many cases where suffixing every async method with Async is simply redundant. If your method returns a Task or Task<T>, and its purpose is to get data from a database or another network resource, then of course it runs asynchronously. The compiler will also almost always give you an error if you forget to await something. With that in mind, there are some good reasons to suffix async methods with Async:

  • The method runs asynchronously but does not return a Task
  • There is an alternative other method by the same name that runs synchronously
  • Forgetting an await in calling code is unlikely to be caught by the compiler

Suffixing a method name with Async is meant to indicate that the method runs asynchronously, which this method does. Whether it does so using async/await or directly returns another Task is an implementation detail. Imagine you were reviewing some code calling the method that forgets to use await. Which of these makes the error more apparent?

var item1 = _example.Get(itemId1);

var item2 = _example.GetAsync(itemId2);

Based on nothing else, there's no reason to believe there's anything wrong with the first line. The second line will at least raise some eyebrows.

Eric B
  • 4,367
  • 5
  • 33
  • 43
  • an asynchronous method should always return a `Task`. `async void` should be reserved for event handlers, and I can't say I would ever call them "manually". Do you have other examples of a method that runs asynchronously but does not return a Task? – default Jan 25 '17 at 16:24
  • not sure about _Forgetting an await in calling code is unlikely to be caught by the compiler_ either. If the calling method is `async` and you call a `Task` method without `await` you will get a warning. Could you maybe explain what you mean by that? – default Jan 25 '17 at 16:26
  • that a method returns a task may be non-obvious if there's a using declaration such as `using SomeType = Task>>`. You can forget an await without being warned by the compiler if the calling code is also not marked as async. – Eric B Jan 25 '17 at 16:54