Mattia Durli
Mattia Durli

Reputation: 767

Defer starting of a Task<T>

I have a series of methods (with variable number of parameters) that return a Task I want to create a method that does something before and after each of this methods, by passing the Task. I've simplified everything (removed cancellationToken, actual processing, etc) in this sample:

public async Task<string> GetDataString()
{
    Console.WriteLine("Executing");
    return "test";
}


public async Task<T> Process<T>(Task<T> task)
{
    Console.WriteLine("Before");
    var res = await task;
    Console.WriteLine("After");
    return res;
}

And in my main:

Task<string> task = GetDataString();
string result = await Process<string>(tasks);
Console.WriteLine(res);

the console output is

Executing
Before
After
test

What can I do to create the task but not actually starting it? And starting it only before the wait?

I managed to do it by creating a PauseToken, as explained in this article: https://devblogs.microsoft.com/pfxteam/cooperatively-pausing-async-methods/ but I wonder if is there a better way.

Thanks, Mattia

Upvotes: 5

Views: 844

Answers (2)

Ramin
Ramin

Reputation: 87

You can remove the async keyword (from GetDataString) and create a new task which will be executed when you await

so the result of the code below is : before , executing , test , after

      private static async Task Start()
        {
            Task<string> task = GetDataString();
            string result = await Process<string>(task);
            Console.WriteLine(result);

            Console.ReadLine();
        }


        public Task<string> GetDataString()
        {
            return new TaskFactory(TaskScheduler.Default).StartNew(() =>
            {
                Console.WriteLine("Executing");
                return "test";
            });
        }


        public async Task<T> Process<T>(Task<T> task)
        {
            Console.WriteLine("Before");
            var res = await task;
            Console.WriteLine("After");
            return res;
        }

Upvotes: -1

Theodor Zoulias
Theodor Zoulias

Reputation: 43495

Your generic ProcessAsync method could accept a task factory as argument:

public async Task<T> ProcessAsync<T>(Func<Task<T>> taskFactory)
{
    Console.WriteLine("Before");
    var res = await taskFactory();
    Console.WriteLine("After");
    return res;
}

This way the task will be created at the time you'll invoke the factory method. You are in control of its creation.

Here is an example of calling the ProcessAsync method, passing as factory a lambda:

var result = await ProcessAsync(() => GetDataStringAsync(arg1, arg2));

This way you are not restricted to a factory method without arguments.

For completeness I should mention that Task objects can also created in a cold state using the constructor new Task(), and started later using the Start method, but this approach is not recommended.

Upvotes: 8

Related Questions