0

Im trying to understand how Lambda expression work with async methods.

I have a function

private int Server_Get_Int(){
            Task<int> task = Task.Factory.StartNew<int>( async () => {
                FirebaseClient c = Server_Connect();
                FirebaseResponse response = await c.GetAsync("todos/set");
                return response.ResultAs<int>(); //The response will contain the data being retreived
            } );
            task.Wait();
            int result = task.Result;
            Console.WriteLine(result);
            return result;
        }

I want my async code to run in the lambda expression and get back the result from the server. But i get back the error:

 error CS4010: Cannot convert async lambda expression to delegate type 'Func<int>'. An async lambda expression may return void, Task or Task<T>, none of which are convertible to 'Func<int>'.

It says i can only return a void, task or task<> and to my understanding im returning

task<int>

Is this a problem with what im returning or is this because of async lambda?

Thanks

Edit:

response.ResultAs<int>()

Returns an Int but being inside a Task function it should be returned as a Task

WPFasync
  • 1
  • 1
  • 2
  • What is the type of `response.ResultAs()`? – JohnyL May 06 '18 at 14:39
  • It returns an int but to my understanding when wrapped in a Task wont it be returned as Task? – WPFasync May 06 '18 at 14:44
  • No, you should return namely `int`. Say, you have this code: `Func func = () => 10; Task task = Task.Factory.StartNew(func); task.Wait(); int result = task.Result;` The `result` variable will be 10. I suspect this is something about `ResultAs` method. – JohnyL May 06 '18 at 14:49
  • It could be connected but the API im using doesnt write much about the functions they made. – WPFasync May 06 '18 at 14:55
  • But i think my problem is mostly because of working with async, most problems seems to arise from it. – WPFasync May 06 '18 at 14:56

2 Answers2

2

Your whole method is suboptimal. You could rewrite your code to be much simpler. A few comments on your existing code first, however.

  1. You're using Task.Factory.StartNew(), which is dangerous. In most cases you should simply use Task.Run()
  2. You're using Task.Wait() and Task.Result, which is also suboptimal, not to mention, that Task.Result includes Task.Wait() when accessing it. But I guess, that you want to test the lambda, so it's ok here.

The ResultAs<T>() method converts the response into an int (in your case). The method itself is defined as public virtual T ResultAs<T>(). It needn't return a Task<int>, because your lambda is asynchronous. If you'd remove the async from the lambda you'd have to return a Task<int>, but you can'T do that by simply changing the ResultAs<T> to ResultAs<Task<int>>, you'd have to use a TaskCompletionSource.

Based on the above that we can rewrite your method to this:

private int Server_Get_Int(){
    var task = Task.Run(async () => {
        var c = Server_Connect();
        return (await c.GetAsync("todos/set")).ResultAs<int>();
    });

    int result = task.Result;
    Console.WriteLine(result);
    return result;
}

A more concise approach could look like this:

private async Task<int> Server_Get_Int_Async(){
    return await Task.Run(async () => {
        var c = Server_Connect();
        return (await c.GetAsync("todos/set")).ResultAs<int>();
    });
}

This creates a new task via Task.Run() and returns that to be completed later.


Based on the comments here are tow ways how you'd call the Server_Get_Int_Asnyc() method. I used explicit types so you can follow my comment, but in almost any case it's better to use var, because the compiler than can choose the best type for the job.

public async Task Foo()
{
    // This is the fetch task that's going to be completed sometime in the future. You should almost in any case use configure await on your tasks. For reasons see below.
    Task<int> intTask = Server_Get_Int_Async().ConfigureAwait(false);

    // Do something other with the task object

    // Finally await it and print
    int result = await intTask;
    Console.WriteLine(result);
}


// Do this if you just need the result and nothing else.
public async Task Bar() 
{
    int result = await Server_Get_Int_Async().ConfigureAwait(false);

   Console.WriteLine(result);
}

In the end it seems, you're pretty new to Task based programming with async/await. I recommend you read the (excellent) introduction article written by Stephen Cleary and go on from there. The beauty of async/await is, that it propagates naturally through your code and you can write asynchronous code almost like you'd write synchronous code.

Also, placing another article here on why you shouldn't use Wait() or Result to simply get the return value of the async method, since it's going to be noticed much better: https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

Ruhrpottpatriot
  • 1,058
  • 3
  • 17
  • 31
  • If `Server_Connect` could be made `async` its self, a lot of these hoops could be avoided altogether. – Bradley Uffner May 06 '18 at 15:59
  • Thanks for the in-depth answer! I also wanted to ask how would you call these functions? I call them using 'int test = Server_Get_Int_Async()' and i get an error from 'Task' to 'int'. – WPFasync May 06 '18 at 16:52
  • 1
    Which is absolutely correct. `Task` represents a task, that may be completed now, or at any time in the future. To get the result object (in your case `int`) you either need to use `Wait()` (bad!), or make the calling method async as well. I'll update the answer with a usage example. – Ruhrpottpatriot May 08 '18 at 21:23
0

Test code (console app). This correctly shows "Result: 10".

static void Main(string[] args)
{

    Func<Task<int>> func = async () => { await Task.Delay(1000); return 10; };
    var task = Task.Factory.StartNew(func);
    task.Wait();
    int result = task.Unwrap().Result;
    WriteLine($"Result: {result}");
    ReadKey(true);
}
JohnyL
  • 6,894
  • 3
  • 22
  • 41