3

I have the following code in LINQPad:

async Task Main()
{
    await PrintLoop(Print1());
}

async Task Print1()
{
    Debug.WriteLine("Printing!");
}

//Print 2
//Print 3
//etc.

async Task PrintLoop(Task printer, int iterations = 3)
{
    for (int i = 0; i < iterations; i++)
    {
        await printer;
    }
}

I can't for the life of me figure out why I get the following output:

Printing!

As opposed to "Printing!" x3.

If I call Print1() directly within the loop I get the following output:

Printing!
Printing!
Printing!
Printing!

Which kind of makes sense but isn't what I want to do. Instead, I would like for Print1 (or whichever method is passed as task) to be invoked iterations times.

Can someone help me understand what's up and down here? Thanks!

6
  • Tasks aren't invoked. They are a promise that something will complete in the future and maybe produce a value. await doesn't invoke, it awaits the task representing an already running asynchronous operation to complete, without blocking the thread Commented Oct 11, 2019 at 13:30
  • 3
    As for why you only get one Printing! it's because Print() is only called once. It never runs asynchronously since it doesn't have any await calls or any Task.Run() etc. It returns an already completed task that's awaited three times by PrintLoop, essentially doing nothing Commented Oct 11, 2019 at 13:32
  • That makes sense, thank you for the explanation. How would I go about creating a proper PrintLoop then? One that doesn't simply await an already running task but actually invokes and awaits #iterations "instances" of the task provided as a parameter? Commented Oct 11, 2019 at 13:49
  • If you wanted to call it more than once, you would need to pass an Action<Task> rather than a Task so that you can invoke it asynchronously. Commented Oct 11, 2019 at 14:03
  • 3
    @KieranDevlin You mean Func<Task>. Action<Task> is a method that accepts a Task as a parameter and returns void. Commented Oct 11, 2019 at 16:00

1 Answer 1

5

You are passing the result of calling Print1() to the method (a Task). You aren't passing the method itself. So it's only called once at Print1(). When you await printer;, it's really just saying "yup, it happened" and moving on.

If you want to pass the method itself, so that it can be called inside PrintLoop, then you need to accept a Func<Task> (a method that returns a Task).

Then you pass the method itself (Print1) without calling it (not Print1())

async Task Main()
{
    await PrintLoop(Print1); //not Print1()
}

async Task Print1()
{
    Debug.WriteLine("Printing!");
}

async Task PrintLoop(Func<Task> printer, int iterations = 3)
{
    for (int i = 0; i < iterations; i++)
    {
        await printer();
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.