Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Task.FromResult v/s await in C#

I am new to C# async programming and need to see if which of the following is a preferred way to deal with Task object.

I have a class that does this:

var value = this.SomeFunction(...);

var innerValue = await Task.FromResult(value.Result);

Somefunction looks like below.

protected async Task<JObject> Somefunction(..)
{
 ..
 returns JBoject
 ..
}

This works fine as expected. Now, I have a suggestion that I shouldn't be using Task.FromResult with async functions. Instead, I should be using something like:

var innerValue = await value; //..this works fine too

But I am not really sure why the 1st one isn't a good practice to get the same thing done. Any leads would be helpful. Thanks

like image 565
learntogrow-growtolearn Avatar asked Jun 06 '18 18:06

learntogrow-growtolearn


2 Answers

Let's go through what you're doing and say precisely why it is wrong.

var value = this.SomeFunction(...);

First off: use var when the type is either clear or unimportant. The type is not clear here and it is important.

Second: Name functions that are asynchronous with an Async suffix.

Let's fix up your example. It's still wrong, but now it is more clear:

Task<Toast> toastTask = this.StartToasterAsync();
Toast toast = await Task.FromResult(toastTask.Result);

This workflow is completely wrong. Let's translate it into English. Here's my to-do list for today:

  • Put some bread in the toaster.
  • While the bread is toasting I could be doing other work, but instead, stare at the toaster doing nothing else until it is done.
  • Get the toast from the toaster, and start a new to-do list.
  • The new to-do list is: Obtain the piece of toast that I am now holding.
  • Execute that to-do list. While I'm waiting for the to-do list to complete, go do other work, but the to-do list is always already complete because the job is to obtain a result that I have already obtained. So don't do other work. Just check that yes, I am in fact holding the piece of toast I just wrote a to-do list about.
  • Now I have a piece of toast.

This workflow is an insane way to make toast. It works -- you end up with a piece of toast at the end -- but no sensible person would do this, and you should not write the equivalent computer program:

  • It is an "asynchronous" workflow where every possible asynchronous advantage has been removed.
  • The first step -- waiting for the toaster to pop -- has been synchronously waited.
  • The second step -- asynchronously wait for a task that has already completed -- is never asynchronous!

Never never never write asynchronous code this way.

The right workflow for making a piece of toast is:

  • Put the bread in the toaster and start it toasting.
  • Do other work until the toast pops.
  • Fetch the toast.

And as we'd expect, the correct way to write your program is much simpler:

Task<Toast> toastTask = this.StartToasterAsync(...);
Toast toast = await toastTask;

Or even better:

Toast toast = await this.StartToasterAsync(...);

You're new to this and you have not internalized the meanings of the various operations on asynchronous workflows. The operations in your little program are:

  • .Result means stop being asynchronous and do nothing until the result is available. Note that this can cause deadlocks. You are stopping a thread until the result is available; what if you are stopping the thread that was going to produce the result in the future? Imagine for instance that we did a to-do list of "(1) Order a box of chocolates from the internet. (2) Do nothing until the chocolates arrive. (3) Get the chocolates out of the mailbox." That workflow does not complete because there is a synchronous wait for a result that requires you to do work in the future.
  • await means this workflow cannot continue until the result is available so asynchronously wait for it. Go off and find other work to do, and when the result is available, we'll start again here.

Note that both of them mean the same thing, in that both are points in a workflow where the workflow does not proceed until the result is available. But they are completely different in that Result is a synchronous wait, and await is an asynchronous wait. Make sure you understand this. It is the most fundamental point that you must understand.

And finally

  • FromResult means someone needs a task, but I already have the result of that task, so make a task that is already complete. When it is awaited or when Result is called on it, either way, it returns the result immediately.

It is unusual to call FromResult. If you are in an asynchronous workflow, normally you would just return result; to signal that a task was complete.

like image 171
Eric Lippert Avatar answered Sep 28 '22 04:09

Eric Lippert


The problem in the first version is not the use of Task.FromResult, which, from the documentation:

Creates a Task<TResult> that's completed successfully with the specified result

The problem is the call value.Result which performs a synchronous wait. So, you are essentially asynchronously waiting for a synchronous result.

Notice that the first version is a bad wrapper (because of the code that must be generated for the await call) around this:

var value = this.SomeFunction(...);
var innerValue = value.Result;

To sum up, just use this:

var innerValue = await value;

Or, if there's no code to run between value and innerValue, you can ignore the assignment of value altogether

var innerValue = await this.SomeFunction(...);
like image 42
Camilo Terevinto Avatar answered Sep 28 '22 05:09

Camilo Terevinto