Xavier Peña
Xavier Peña

Reputation: 7919

Convert any given function into an awaitable task

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;
}

Upvotes: 21

Views: 27858

Answers (7)

Rohit Goyat
Rohit Goyat

Reputation: 69

You can use:

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

Upvotes: 5

Mario Rancic
Mario Rancic

Reputation: 47

The problem is that CastFuncToTask runs synchronously. if Func is parameterless you can do something like this:

public static Task<T> ToTask(this Func<T> func) => Task.Factory.StartNew(() => func());

Anyway we can play with TaskCompletionSource and return something we want regardless of the method. Lets say we have Action instead of Func

public static Task<string> ToTask(this Action action)    
{    TaskCompletionSource<string> taskCompletionSource = new 
           TaskCompletionSource<string>();
           string result = null;
           try
           {
               Task.Factory.StartNew(() => action());
           }
           catch (Exception ex)
           {
               result = ex.Message;
           }
           taskCompletionSource.SetResult(result);
           return taskCompletionSource.Task;
       }

Upvotes: -2

pravin chand
pravin chand

Reputation: 19

Below code may help others:

Note: GenericMethod is parameter name which we are passing as Func with Input (T1) and output (T2) of generic Type. Task Factory will provide necessary access to run your task.

    public async Task<T2> GenericAsyncCall<T1, T2>(Func<T1, T2> GenericMethod, T1 input)
    {
        var _output = await Task.Factory.StartNew(() => {
            var output = GenericMethod.Invoke(input);
            return output;
        });

        return (T2)_output;
    }

Upvotes: 0

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149646

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

Upvotes: 24

Florian J
Florian J

Reputation: 59

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();

Upvotes: 1

J&#252;rgen Steinblock
J&#252;rgen Steinblock

Reputation: 31743

With a windows forms application I confirmed that the first call is blocking the second isn't. So the best solution would be to use Task.Run() with a lambda expression.

    private async void button1_Click(object sender, EventArgs e)
    {
        button1.Text = "...";
        var result = await Task.FromResult(TestFunc(2));
        button1.Text = result.ToString();
    }

    private async void button2_Click(object sender, EventArgs e)
    {
        button2.Text = "...";
        var result = await Task.Run(() => TestFunc(2));
        button2.Text = result.ToString();
    }

    private int TestFunc(int x)
    {
        System.Threading.Thread.Sleep(5000);
        return x;
    }

The compiler can not know that you don't want to evaluate TestFunc(2) but use it as a delegate and will just execute the method first and Task.FromResult will just wrap a return value in a Task. This is not what you want.

Upvotes: 0

Taher  Rahgooy
Taher Rahgooy

Reputation: 6696

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>.

Upvotes: 2

Related Questions