4

What is the best way to return a completed Task object?

It is possible to write Task.Delay(0), or Task.FromResult<bool>(true) whatever.

But what is the most efficient way?

Andrii
  • 1,081
  • 1
  • 11
  • 24
  • What is the reason for this? You don't need a Task for this. just call the method. – I4V May 06 '13 at 21:54
  • 3
    @I4V it is common practice when sometimes an operation needs work but sometimes it isn't and can be done efficiently without needing async – Marc Gravell May 06 '13 at 21:58
  • Here's a good article on edge-cases like this: https://blog.stephencleary.com/2016/12/eliding-async-await.html. tl;dr it's okay to leave the warning, as this approach will avoid a lot of the pitfalls of removing the `async` keyword. – Patrick Roberts Oct 30 '19 at 21:58
  • _"attempt to use return Task.FromResult(mockToken); gives: error CS4016"_ -- that's why you don't make the method `async` when you just return a task object. – Peter Duniho Oct 30 '19 at 22:01
  • 1
    There is a flaw in your question. You said "When async Task required by interface", but an interface can never require `async`. It can only require that the return value is `Task`. – Gabriel Luci Oct 30 '19 at 23:58
  • Yip I realized a day later, just drop the async and the method signature is still valid for the interface. The answer comment regarding exception semantics was also partially what I was concerned about. – enorl76 Oct 31 '19 at 20:28

4 Answers4

5

Answer from Stephen Toub (MSFT):

If you want a new Task object each time, Task.FromResult is the most efficient. Task.Delay(0) in its current implementation will return a cached task, but that's an implementation detail. If you want to use a cached task, you should cache one yourself, e.g. private static readonly Task s_completedTask = Task.FromResult(true); and then use s_completedTask.

Andrii
  • 1,081
  • 1
  • 11
  • 24
4

Task.FromResult would be the most direct. It also includes inbuilt results for a few common integers etc. However, if your value is not an "obvious" value (and won't have inbuilt handling) but is likely to be returned often in your scenario - then you can create your own cached result in a field (maybe static if appropriate) - but it is important to cache the Task, not the result itself.l - otherwise just use Task.FromResult each time.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
3

Here's a little demo which shows the difference in exception handling between methods marked and not marked with async.

public Task<string> GetToken1WithoutAsync() => throw new Exception("Ex1!");

// Warning: This async method lacks 'await' operators and will run synchronously. Consider ...
public async Task<string> GetToken2WithAsync() => throw new Exception("Ex2!");  

public string GetToken3Throws() => throw new Exception("Ex3!");
public async Task<string> GetToken3WithAsync() => await Task.Run(GetToken3Throws);

public async Task<string> GetToken4WithAsync() { throw new Exception("Ex4!"); return await Task.FromResult("X");} 


public static async Task Main(string[] args)
{
    var p = new Program();

    try { var task1 = p.GetToken1WithoutAsync(); } 
    catch( Exception ) { Console.WriteLine("Throws before await.");};

    var task2 = p.GetToken2WithAsync(); // Does not throw;
    try { var token2 = await task2; } 
    catch( Exception ) { Console.WriteLine("Throws on await.");};

    var task3 = p.GetToken3WithAsync(); // Does not throw;
    try { var token3 = await task3; } 
    catch( Exception ) { Console.WriteLine("Throws on await.");};

    var task4 = p.GetToken4WithAsync(); // Does not throw;
    try { var token4 = await task4; } 
    catch( Exception ) { Console.WriteLine("Throws on await.");};
}
// .NETCoreApp,Version=v3.0
Throws before await.
Throws on await.
Throws on await.
Throws on await.

Moved (and edited) from When async Task<T> required by interface, how to get return variable without compiler warning)

tymtam
  • 31,798
  • 8
  • 86
  • 126
  • Except that will change the behavior of `GetToken()` in the event that `Guid.NewGuid().ToString()` throws. – Patrick Roberts Oct 30 '19 at 22:00
  • `Guid.NewGuid().ToString()` doesn't throw. – tymtam Oct 30 '19 at 22:02
  • Even if it did throw, I don't feel it matters: the exact exceptional behavior would be different, but not in any material way. In either case, the caller will see the exception; in the above example, it will be on the call to `GetToken()`, while if the method is left as `async`, it will be on the `await`. Frankly, the former is better; fail-fast. – Peter Duniho Oct 30 '19 at 22:04
  • I'm just pointing out that there are scenarios in which this advice will not produce exact equivalent behavior. Knowing whether `GetToken()` can throw an exception synchronously, or will always return a `Task` is an important consideration to users extrapolating from this answer. – Patrick Roberts Oct 30 '19 at 22:07
  • @PatrickRoberts It's a fair and an important point. I added a little demo to showcase the concern you brought to light ;) – tymtam Oct 30 '19 at 22:21
  • Solutions B and C have a performance overhead (minimal, but it's still there) for no benefit. – Gabriel Luci Oct 30 '19 at 23:55
3

I think that I am a little late. However, I still want to share this answer, for an updated version.

If you want to return a completed task, you should use the following:

return Task.CompletedTask;

This will also automatically set the status to RanToCompletion.

Flimtix
  • 356
  • 1
  • 4
  • 17