Task class has a static method called FromResult which returns an already completed (at the RanToCompletion status) Task object. I have seen a few developers "await"ing on Task.FromResult method call and this clearly indicates that there is a misunderstanding here. I'm hoping to clear the air a bit with this post.
@ 02-24-2014
by Tugberk Ugurlu


Task class has a static method called FromResult which returns an already completed (at the RanToCompletion status) Task object. I have seen a few developers "await"ing on Task.FromResult method call and this clearly indicates that there is a misunderstanding here. I'm hoping to clear the air a bit with this post.

What is the use of Task.FromResult method?

Imagine a situation where you are implementing an interface which has the following signature:

public interface IFileManager
{
     Task<IEnumerable<File>> GetFilesAsync();
}

Notice that the method is Task returning which allows you to make the return expression represent an ongoing operation and also allows the consumer of this method to call this method in an asynchronous manner without blocking (of course, if the underlying layer supports it). However, depending on the case, your operation may not be asynchronous. For example, you may just have the files inside an in memory collection and want to return it from there, or you can perform an I/O operation to retrieve the files list asynchronously from a particular data store for the first time and cache the results there so that you can just return it from the in-memory cache for the upcoming calls. These are just some scenarios where you need to return a successfully completed Task object. Here is how you can achieve that without the help of Task.FromResult method:

public class InMemoryFileManager : IFileManager
{
    IEnumerable<File> files = new List<File>
    {
        //...
    };

    public Task<IEnumerable<File>> GetFilesAsync()
    {
        var tcs = new TaskCompletionSource<IEnumerable<File>>();
        tcs.SetResult(files);

        return tcs.Task;
    }
}

We here used the TaskCompletionSource to produce a successfully completed Task object with the result. Therefore, the caller of the method will immediately have the result. This was what we had been doing till the introduction of .NET 4.5. If you are on .NET 4.5 or above, you can just use the Task.FromResult to perform the same operation:

public class InMemoryFileManager : IFileManager
{
    IEnumerable<File> files = new List<File>
    {
        //...
    };

    public Task<IEnumerable<File>> GetFilesAsync()
    {
        return Task.FromResult<IEnumerable<File>>(files);
    }
}

Should I await Task.FromResult method calls?

TL;DR version of the answer: absolutely not! If you find yourself in need to using Task.FromResult, it's clear that you are not performing any asynchronous operation. Therefore, just return the Task from the Task.FromResult output. Is it dangerous to do this? Not completely but it's illogical and has a performance effect.

Long version of the answer is a bit more in depth. Let's first see what happens when you "await" on a method which matches the pattern:

IEnumerable<File> files = await fileManager.GetFilesAsync();

This code will be read by the compiler as follows (well, in a simplest way):

var $awaiter = fileManager.GetFilesAsync().GetAwaiter();
if(!$awaiter.IsCompleted) 
{
     DO THE AWAIT/RETURN AND RESUME
}

var files = $awaiter.GetResult();

Here, we can see that if the awaited Task already completed, then it skips all the await/resume work and directly gets the result. Besides this fact, if you put "async" keyword on a method, a bunch of code (including the state machine) is generated regardless of the fact that you use await keyword inside the method or not. Keeping all these facts in mind, implementing the IFileManager as below is going to cause nothing but overhead:

public class InMemoryFileManager : IFileManager
{
    IEnumerable<File> files = new List<File>
    {
        //...
    };

    public async Task<IEnumerable<File>> GetFilesAsync()
    {
        return await Task.FromResult<IEnumerable<File>>(files);
    }
}

So, don't ever think about "await"ing on Task.FromResult or I'll hunt you down in your sweet dreams :)

References




Hi 🙋🏻‍♂️ I'm Tugberk Ugurlu.
Coder 👨🏻‍💻, Speaker 🗣, Author 📚, Microsoft MVP 🕸, Blogger 💻, Software Engineering at Deliveroo 🍕🍜🌯, F1 fan 🏎🚀, Loves travelling 🛫🛬
Lives in Cambridge, UK 🏡