Why you should not use “ContinueWith” in your async code

Are you using ContinueWith in your async code?

Joni 【ジョニー】
2 min readOct 1, 2018

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 ContinueWith doesn’t do this unless you specify the right set of flags.
  • Calling ContinueWith allocates 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.

For example:

if (!task.IsCompleted)
{
return FinishAsync(task);
}
private async Task FinishAsync(Task task)
{
try
{
await task;
}
catch (Exception ex)
{
Log(.....)
}
}
  • ContinueWithallocates more than using async await. In fact, in .NET Core Task is very optimized for async await code paths and allocates less than ContinueWith.
  • 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 ContinueWith in 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 struct and 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

Summary

Use async/await.

--

--