Skip to content

Analyzer proposal: remove redundant state machine #518

Closed

Description

Call out where an async state machine is redundant:

async Task<string> DoSomethingAsync()
{
   return await SomethingElseAsync(); // .ConfigureAwait(bool) might be a suffix
}

So long as SomethingElseAsync() returns the same type as the caller, you can abbreviate to:

Task<string> DoSomethingAsync()
{
   return SomethingElseAsync();
}

Above is almost equivalent, but it doesn't guarantee exceptions are caught and returned as exceptions, so strictly speaking this is more equivalent:

Task<string> DoSomethingAsync()
{
   try
   {
      return SomethingElseAsync();
   }
   catch (Exception ex)
   {
      return Task.FromException(ex);
   }
}

Cases that cannot be simplified include;

  1. more than one await
  2. The await is not immediately returned (or not the last statement).
  3. The await appears inside a try block.
  4. A using var appears in the method, such that it will be disposed of prior to the returned Task being completed.
  5. The type returned by the called async method is not exactly the same as the type returned from the calling async method. (e.g. Task<object> FooAsync() => BarAsync(); where BarAsync() returns Task<string> or ValueTask<object>.

Activity

  1. mavasani commented on Sep 17, 2019

    @mavasani

    Probably should offer 2 code fixes, with an additional comment around the try-catch for the more complex code fix?

  2. drewnoakes commented on Sep 17, 2019

    @drewnoakes
    Member

    I generally make this change manually and would appreciate the analyzer, but we should be aware that it changes the debugging experience by removing DoSomethingAsync from the call stack when the program is executing a continuation in SomethingElseAsync.

  3. sharwell commented on Nov 23, 2019

    @sharwell
    Contributor

    Removing async also disables the compiler warning saying a task should be awaited, so you'll definitely want to make sure that analyzer is working well before suggesting that async get removed.

  4. paulomorgado commented on Dec 2, 2019

    @paulomorgado
    Contributor

    This is a performance optimization but it causes a change in the stack trace if an exception is thrown.

    Check this post from Stephen Cleary (@StephenCleary):

    Eliding Async and Await

  5. NN--- commented on Sep 30, 2020

    @NN---

    ReSharper extensiosn ReCommended Extension and AsyncConverter suggesting to remove async/await.
    This is indeed changes the stack trace but it is still a useful analysis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions