Return a Task instead of awaiting the inner method call
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In C#, a method that only forwards another asynchronous call often does not need async and await at all. Returning the inner Task directly keeps the code shorter and avoids creating an unnecessary async state machine. The important caveat is that this is only safe when the wrapper truly adds no behavior of its own.
The Simple Pass-Through Case
These two methods look similar, but only one of them does unnecessary work.
If the wrapper is only forwarding the call, the first version is normally preferable. It expresses the intent directly and avoids extra machinery.
await Changes Behavior, Not Just Style
The reason this question matters is that await is not merely syntax decoration. It changes control flow, exception timing, and resource lifetime.
For example, consider error handling:
If you rewrite this as return _repository.CountAsync();, the catch no longer handles asynchronous failures that occur after the method returns the task. That is a real behavior change.
Resource Lifetime Is Another Boundary
You usually need await when a resource must stay alive until the async operation finishes.
This method must stay async. If you tried to return the inner task from inside the using scope, the response could be disposed before the read completed.
The same rule applies to:
- '
usingandawait using' - '
tryandfinally' - transactions
- locks or other scoped state
Keep await When the Wrapper Adds Logic
A wrapper should remain async if it transforms the result, validates input, logs completion, or performs several async steps.
This is not a pass-through method. It adds validation and result shaping, so await belongs there.
A Practical Rule of Thumb
Return the inner task directly when the method is only forwarding a call:
Keep await when you need any of the following:
- exception handling around the asynchronous operation
- resource lifetime that must extend until completion
- result transformation
- multiple asynchronous steps
- completion-dependent logging or metrics
That rule is simple and usually enough for real code.
Performance Is Usually Secondary
Yes, removing an unnecessary async state machine can be a minor optimization. But in most application code, the more important benefit is clarity. A direct Task return says, very plainly, “this method is only forwarding the operation.”
Do not remove await mechanically across a codebase. Remove it where the wrapper is genuinely redundant.
Common Pitfalls
- Returning the inner task from a method that relies on
tryandcatchto handle async failures. - Returning a task from inside a
usingscope and disposing resources too early. - Treating this as a universal performance rule instead of checking whether the wrapper adds behavior.
- Keeping
asyncandawaiton methods that are pure pass-throughs. - Forgetting that stack traces and debugging behavior can look different between the two forms.
Summary
- Return the inner
Taskdirectly when the method is a pure pass-through. - Keep
awaitwhen the method handles exceptions, manages resources, or transforms results. - '
awaitchanges behavior, not just syntax.' - The main benefit of direct task return is clearer intent, with a small performance benefit as a side effect.
- Thin wrapper methods are the best candidates for this simplification.

