3

Consider the following method:

public async Task<string> GetNameOrDefaultAsync(string name)
{
    if (name.IsNullOrDefault())
    {
        return await GetDefaultAsync();
    }

    return name;
}

When name is provided, no awaiting will occur in the method execution, yet this method will compile correctly.

However, this method will produce the build warning shown below:

public async Task<string> GetDefaultAsync()
{
    return "foobar";
}

[CS1998] This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Why is it that GetNameOrDefaultAsync can return without ever awaiting, and not result in a compiler warning, but GetDefaultAsync must await in order to compile?

Would it be an improvement to do the following?:

public async Task<string> GetNameOrDefaultAsync(string name)
{
    if (name.IsNullOrDefault())
    {
        return await GetDefaultAsync();
    }

    return await Task.FromResult(name);
}

Here we would have no execution paths that don't await something.

  • 1
    Its not a compile error, its just a warning that you might have done something you didn't intend. It is OK to never await an async call, like for a fire-and-forget operation if that is what you really intend. – Crowcoder May 07 '21 at 15:05
  • @Crowcoder thanks for that clarification. I updated the question to specify it's a warning, not error. Regarding not awaiting, I wouldn't consider this a fire-and-forget operation, because we do care about the string result, and we would await while consuming `GetNameOrDefaultAsync` – Benjamin Leeds May 07 '21 at 15:09

1 Answers1

6

Why is it that GetNameOrDefault can return without ever awaiting, and not result in a compiler warning

Because there are execution paths that do use await, and to be able to use await the method needs to be async.

On the other hand, if there are no paths that use await, the async keyword is clearly superfluous, hence the warning.

Would it be an improvement to do the following? return await Task.FromResult(name);

No, Task.FromResult simply wraps name in a Task object, which is in a completed state, meaning the await unwraps it again immediately.

This allocates a Task unnecessarily, and achieves nothing.


As an aside, if name.IsNullOrDefault() is almost always false, i.e. using await is the exception not the rule, you could reduce allocations by using ValueTask instead: Why would one use Task<T> over ValueTask<T> in C#?

Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35