Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async method with no await vs Task.FromResult

Consider the following interface:

public interface IProvider
{
    Task<bool> Contains(string key);
}

This is implementation satisfies Visual Studio

public Task<bool> Contains(string key)
{
    return Task.FromResult(false);
}

This implementation is convenient to write and would seem to achieve the same thing:

public async Task<bool> Contains(string key)
{
    return false;
}

However, Visual Studio throws a hissy-fit and insists:

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await TaskEx.Run(...)' to do CPU-bound work on a background thread.

I'd love to just ignore that warning and avoid using Task.FromResult(...).

Are there any negative consequences to using the latter option?

like image 295
Sigurd Garshol Avatar asked Sep 03 '18 08:09

Sigurd Garshol


People also ask

Can We await the result of an async method in Java?

This means that one can await the result of an async method because it returns a Task, not for being marked as async. A method can return async Task, Task, or void. It is advised to return void only when necessary since the Tasks are awaitable, while void is not.

What is the use of the fromresult async method?

The FromResult async method is a placeholder for an operation that returns a DayOfWeek. When GetLeisureHoursAsync is called from within an await expression in the ShowTodaysInfo method, the await expression retrieves the integer value (the value of leisureHours) that's stored in the task returned by the GetLeisureHours method.

How do you return a task from an async method?

If you use a Task return type for an async method, a calling method can use an await operator to suspend the caller's completion until the called async method has finished. In the following example, the WaitAndApologize async method doesn't contain a return statement, so the method returns a Task object.

What does a= await taskfromresult mean in JavaScript?

a= await TaskFromResult (x) is just an inefficient way of writing a = x. I think it might depend if you are already in an async method. awaits can still return synchronously if the task is completed, so there wouldn’t be a context switch.


1 Answers

The reason for that "hissy fit" is that the compiler needs to do a lot of work to present a task that works in all the expected right ways here, which you can see by compiling and decompiling it

Task.FromResult is cleaner, but may still have overhead - IIRC there are some scenarios where a Task.FromResult might work efficiently here (returning the same object each time), but I wouldn't rely on it.

There are 2 pragmatic reliable approaches:

  • return a reused static Task<bool> result each time
  • use ValueTask<bool> - which seems ideal here if you are returning synchronously a lot of the time

i.e.

private readonly static Task<bool> s_False = Task.FromResult(false);
public Task<bool> Contains(string key, string scope)
{
    return s_False ;
}

or

public ValueTask<bool> Contains(string key, string scope)
{
    return new ValueTask<bool>(false);
}

Note: the second of these may not be possible in this case, since you didn't define the interface. But: if you ever are designing an interface that needs to allow async usage but which may actually be sync: consider using ValueTask<T> as the exchange type, not Task<T>.

The generated C# of:

public async System.Threading.Tasks.Task<bool> Contains(string key, string scope)
{
    return false;
}

is something like:

[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <Contains>d__0 : IAsyncStateMachine
{
    public int <>1__state;

    public AsyncTaskMethodBuilder<bool> <>t__builder;

    private void MoveNext()
    {
        bool result;
        try
        {
            result = false;
        }
        catch (Exception exception)
        {
            <>1__state = -2;
            <>t__builder.SetException(exception);
            return;
        }
        <>1__state = -2;
        <>t__builder.SetResult(result);
    }

    void IAsyncStateMachine.MoveNext()
    {
        //ILSpy generated this explicit interface implementation from .override directive in MoveNext
        this.MoveNext();
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine stateMachine)
    {
        <>t__builder.SetStateMachine(stateMachine);
    }

    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
    {
        //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
        this.SetStateMachine(stateMachine);
    }
}

[AsyncStateMachine(typeof(<Contains>d__0))]
public Task<bool> Contains(string key, string scope)
{
    <Contains>d__0 stateMachine = default(<Contains>d__0);
    stateMachine.<>t__builder = AsyncTaskMethodBuilder<bool>.Create();
    stateMachine.<>1__state = -1;
    AsyncTaskMethodBuilder<bool> <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}
like image 145
Marc Gravell Avatar answered Oct 29 '22 13:10

Marc Gravell