17

Suppose to have a service library with a method like this

public async Task<Person> GetPersonAsync(Guid id) {
  return await GetFromDbAsync<Person>(id);
}

Following the best practices for the SynchronizationContext is better to use

public async Task<Person> GetPersonAsync(Guid id) {
  return await GetFromDbAsync<Person>(id).ConfigureAwait(false);
}

But when you have only one operation (I think) is better to return the Task directly. See At the end of an async method, should I return or await?

public Task<Person> GetPersonAsync(Guid id) {
  return GetFromDbAsync<Person>(id);
}

In this last case you can't use ConfigureAwait(false) because the method is not awaited.

What is the best solution (and why)?

Community
  • 1
  • 1
sevenmy
  • 437
  • 2
  • 6
  • 13
  • 1
    I think the last one (delegation) is the clearest and does not involve creation of additional state machine. Unless you're doing *something else* inside the method that's dependent on async call result, I see no point in using `await`. – Patryk Ćwiek May 08 '14 at 08:44
  • The last one makes the most sense to me. It returns a Task which you can await from wherever you're calling GetPersonAsync – Dennis_E May 08 '14 at 08:46
  • So the solution that return the Task directly doesn't capture the SynchronizationContext? – sevenmy May 08 '14 at 08:56
  • 1
    What you're doing is allowing your caller to decide how *they* want to wait for that `Task` to complete - if *that* code is `async`, *it* can decide whether to capture context when `await`ing the result. – Damien_The_Unbeliever May 08 '14 at 09:03
  • @Damien_The_Unbeliever ok, thus in the latest example there's not the switching context overhead? – sevenmy May 08 '14 at 09:24

1 Answers1

11

Each option has its own specifics, check this and this. If you understand them, you could decide what's the best one for you.

So the solution that return the Task directly doesn't capture the SynchronizationContext?

It's not the task that captures the current synchronization context. It's TaskAwaiter.OnCompleted (or ConfiguredTaskAwaitable.OnCompleted, in case of ConfigureAwait), which is indirectly invoked by the code generated by C# compiler as a part of the await statement for the task.

So, if you don't use await, you shouldn't be worried about SynchronizationContext capturing, it doesn't magically happen on its own. This probably makes the 3rd option the most favorable one, but keep in mind its exception propagation behavior.

Community
  • 1
  • 1
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • thanks. I think this is a very domain specific choice. Do any difference if the method called is really async? For example an async query with EF (the error handling is important). – sevenmy May 08 '14 at 10:50
  • 1
    Another good reading is: [Async Performance: Understanding the Costs of Async and Await](http://msdn.microsoft.com/en-us/magazine/hh456402.aspx) – sevenmy May 08 '14 at 10:52
  • @sevenmy, An `async Task` method would never throws until you observe the task with `await task`, `task.Wait()`, or `task.Result`. OTOH, a non-async `Task` method may throw immediately. **However**, in your client code, you should make no assumptions. Always code like it can throw either synchronously (on the same stack frame) or asynchronously. – noseratio May 08 '14 at 11:01