Why you should not use “ContinueWith” in your async code
For my future reference, this post is a quick summary of ASP.NET Core Architect David Fowler’s tweets:
So, here we go.
- Async state machines, though they have creation overhead are easier to debug. They can and will be further optimized in the future (on the runtime side).
- They execute synchronously if the task is already complete, something that
ContinueWithdoesn’t do this unless you specify the right set of flags.
ContinueWithallocates another task per operation (it wraps your delegate in a task object) instead of re-using the state machine instance as the continuation. So you’re static callback is then wrapped in a ContinuationTask object. That then also gets wrapped in another continuation object and attached to the list of Task continuations…. Task itself is also is optimized for async/await over everything else.
}private async Task FinishAsync(Task task)
catch (Exception ex)
ContinueWithallocates more than using async await. In fact, in .NET Core Task is very optimized for async await code paths and allocates less than
- The state machine’s overhead is a concern when you finish synchronously, not asynchronously.
- If you take into account the whole “cost,” including the state machine generation, async/await is still lighter than just using
ContinueWithin this situation. At least on .NET Core it is.
ContinueWithneeds to capture the execution context. That’s going to mean at least an object that has both your callback and options.
- In the async await case, the state machine starts off as a
structand once you go async it is then boxed into an object, that boxing allocation is basically reused for everything.
Last but not least, also worth to check tweet thread:
[Updates] Check out this Async Guidance from David Fowler ✨