Convert any given function into an awaitable task

19,453

Solution 1

Running .NET 4.5, you can greatly simplify your code by doing:

int task = await Task.FromResult(TestFunc(2));

No need to wrap it yourself in a TaskCompletionSource.

I am aware that there could be many things wrong with the concept behind code.

If what you're trying to do is asynchronously query your database, this solution will definitely not help. It only artificially wraps your result in a Task. If you really want to query your database asynchronously, you need to use async methods provided by your database provider.

If you're using MySQL and looking for a driver that supports async, look into Dapper

Solution 2

You can use:

await Task.Run(() => obj.functionname());

Solution 3

change it to

int task = await CastFuncToTask<int>(()=>TestFunc(2));

In your code the input provided for CastFuncToTask is an int(what TestFunc returns.) but it needs a delegate Func<T>.

Solution 4

my method is this :

public Task<int> SetOffAsync()
{
    return Task<int>.Factory.StartNew(() =>
    { 
        /*do something else*/
        return 42;
    });
}

and you can call this :

int var = await SetOffAsync();
Share:
19,453
Xavier Peña
Author by

Xavier Peña

Updated on June 05, 2022

Comments

  • Xavier Peña
    Xavier Peña almost 2 years

    The goal of the following code is to cast any given function into an awaitable function. The idea is to use it when fetching the data from the db, giving the code the flexibility to either use the synchronous fetch functions (an imposition of my current ORM), or use the very same functions as async.

    I am aware that there could be many things wrong with the concept behind code. By now I was just trying to get rid of the compiler errors so I can run the code and check the behavior. But of course I am open to discuss the concept beforehand, and if the whole idea behind it is wrong then use my time more efficiently looking for another solution.

    async static void Main()
    {
        // The following line gives a compiler error:
        // Error    1   The best overloaded method match for 'CastFuncToTask<int>(System.Func<int>)' has some invalid arguments 
        int task = await CastFuncToTask<int>(TestFunc(2));
    }
    
    private static Task<T> CastFuncToTask<T>(Func<T> func)
    {
        TaskCompletionSource<T> taskCompletionSource = new TaskCompletionSource<T>();
        T result = func.Invoke();
        taskCompletionSource.SetResult(result);
        return taskCompletionSource.Task;
    }
    
    private static int TestFunc(int testInt) 
    {
        return testInt * 2;
    }
    
  • Jürgen Steinblock
    Jürgen Steinblock over 8 years
    I doupt that this will work. TestFunc will be evaluated prior to passing the result to Task.FromResult I think T.Rahgooy's answer is better. This would only work for parameterless functions: Task.FromResult(TestFunc)
  • Yuval Itzchakov
    Yuval Itzchakov over 8 years
    @SchlaWiener That's exactly what his method is doing. He's invoking the method and wrapping it in a Task.
  • Yuval Itzchakov
    Yuval Itzchakov over 8 years
    @Eser That was my original answer. It was mistakenly edited :)
  • Xavier Peña
    Xavier Peña over 8 years
    @SchlaWiener both users YuvalItzchakov (in this answer) and SriramSakthivel (in the comments of the question) were pointing out that I was reinventing the wheel, but now you are saying that this built-in solution might not be suitable to my needs. Egos aside, I would like to know what is my best option.
  • Yuval Itzchakov
    Yuval Itzchakov over 8 years
    @XavierPeña No ego in my answer. The only difference is that you invoke TestFunc internally inside your method, and I invoke it externally and pass the result to Task.FromResult.
  • Eser
    Eser over 8 years
    @XavierPeña put your code in a WinForms application and see the result (also add some long running code to TestFunction). Both solutions will block your UI thread....
  • Xavier Peña
    Xavier Peña over 8 years
    @YuvalItzchakov I was talking about my ego :) (it would be nice to know that I was indeed not trying to reinvent the wheel, but I want to know what is best for my code regardless). So in your opinion using the customized cast would be better than using the built-in Task.FromResult?
  • Yuval Itzchakov
    Yuval Itzchakov over 8 years
    @XavierPeña Although this isn't a cast, I would still advise you not to, since there isn't really a need. Read my edited answer, I'm not sure this will get you to what you really want to do.
  • Jürgen Steinblock
    Jürgen Steinblock over 8 years
    The code above does the same as var x = TestFunc(2); var result = await Task.FromResult(x); I might be wrong but I am pretty sure TestFunc is evaluated before the threading happens.
  • Yuval Itzchakov
    Yuval Itzchakov over 8 years
    @SchlaWiener There is no threading here. You're right, TestFunc is evaulated first, that's what I said. His example does exactly the same.
  • Yuval Itzchakov
    Yuval Itzchakov over 8 years
    Wrapping a synchronous method with Task.Run to do IO bound work is rarely a good idea.
  • Xavier Peña
    Xavier Peña over 8 years
    I agree that Task.FromResultseems to evaluate the function synchronously. But Task.Run opens a new thread, thus not taking full advantage of the async-wait... maybe there is no "good" solution to this problem.
  • Yuval Itzchakov
    Yuval Itzchakov over 8 years
    @XavierPeña There is, use an async API. Which database are you using?
  • Jürgen Steinblock
    Jürgen Steinblock over 8 years
    I think Task.Run() was designed for such situations. Yes you have an overhead but that's true for many await/async scenarios. Task.Run is just a shortcut to Task.StartNew (which makes it more obvious that a new task is created. There is a good article about that on msdn: blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
  • Xavier Peña
    Xavier Peña over 8 years
    @YuvalItzchakov well yes, that's true... using MySQL through NPoco. NPoco has async fetch but does not let the use of linq for this particular case (strangely it does when using the sync fetch). It's been a bit nightmerish to find a good-reliable-fast ORM... would be a bit of a hassle to switch again. Besides, my original question was generic on purpose, since there are potentially other cases in which this "awaitable cast" would be useful in my code.
  • Xavier Peña
    Xavier Peña over 8 years
    As SchlaWiener was pointing out in another answer: "Task.Run is just a shortcut to Task.StartNew (which makes it more obvious that a new task is created.". And as I was saying: "Task.Run opens a new thread, thus not taking full advantage of the async-wait...".
  • Florian J
    Florian J over 8 years
    i'm writing that at the same time
  • Theodor Zoulias
    Theodor Zoulias over 2 years