Home > database >  Does order of calling matter in terms of Task.CompletedTask?
Does order of calling matter in terms of Task.CompletedTask?

Time:02-18

What are the differences? What problems I am introducing with these each approach? Is there any better way to 'wrap' sync call?

async Task Method()
{
    await Task.CompletedTask;
    externalService.syncCall();
}
async Task Method()
{
    externalService.syncCall();
    await Task.CompletedTask;
}
Task Method()
{
    externalService.syncCall();
    return Task.CompletedTask;
}

CodePudding user response:

On the assumption that this method needs to return a Task due to interface constraints:

The first 2 are loosely equivalent and will behave in the same way.

The third will be slightly more efficient because it bypasses the state machine generated by the async keyword, however, eliding async makes you responsible for exception handling, and the current implementation is likely incomplete.

The consumer of an asynchronous (Task-returning) method expects exceptions to be thrown only at the point the Task is awaited.

For example:

var task = SomeMethodAsync();

// Do some other stuff..

await task; // Exception here if thrown in SomeMethodAsync()

Now if externalService.syncCall() throws in your Method(), this will happen:

var task = Method(); // Exception thrown here

// Do some other stuff..

await task;

To ensure Method() behaves as per the first example, you could do something like this:

Task Method()
{
    try { externalService.syncCall(); }
    catch (Exception e) { return Task.FromException(e); }
    return Task.CompletedTask;
}

CodePudding user response:

What are the differences?

The first two are essentially the same, although they're both doing unnecessary work with the await Task.CompletedTask;. I would say await Task.CompletedTask is a code smell, since it's really just adding code to do a noop in order to make a compiler warning go away. The compiler warning is there for a reason, and adding code just to silence it is missing the point of the warning.

The third one is quite different as soon as exceptions come into play. With Task-returning methods, callers expect exceptions to be placed on the returned task, but this one will throw them directly before returning the task.

Is there any better way to 'wrap' sync call?

The best way is to not:

void Method() => externalService.syncCall();

But sometimes you may need to. E.g., if you're implementing an interface that has a Task-returning Method. In that case, I recommend using a #pragma to disable the compiler warning. This has two advantages over await Task.CompletedTask: first, it explicitly notes in the code "I have considered this warning and evaluated it as spurious in this situation"; second, it doesn't add actual code just to do a noop.

#pragma warning disable 1998 // This async method lacks 'await' operators and will run synchronously.
async Task Method()
#pragma warning restore 1998 // This async method lacks 'await' operators and will run synchronously.
{
    externalService.syncCall();
}

If you need to do this often, collecting the #pragmas into a helper type would be cleaner.

  • Related