Friday, October 25, 2024

Using Task.WhenAny to time bound a task

Just for a future reference - suppose a task should not take longer than ... but there's still a need to finish it. It's just the caller that cannot wait longer
One of use cases involves a REST service where you provide the server part. There's a client that should get an answer in max X seconds, regardless of whether the actual task finishes or not. You want a timer that runs together with the task and in case any of the two finishes first, you want to know which one was it.
Let's stage actors first:
public class TaskExperiments
{
	public async Task<string> DelayWithEcho( int ms, string echo, string id )
	{
		await Task.Delay( ms );
		Console.WriteLine( echo );

		return id;
	}
}
This one above is supposed to be a factory of tasks. A task is given a time and an id so that when it finishes, I know the id.
Now, the runner:
var e = new TaskExperiments();

var t1 = e.DelayWithEcho( 5000, "Task1 after 5000ms - time constraint", "i1" );
var t2 = e.DelayWithEcho( 3000, "Task2 after 3000ms - actual task",     "i2" ); 

var t  = await Task.WhenAny( t1, t2 );
var id = await t;

Console.WriteLine( $"Finished one of the two : {id}" );
As you can see, Task.WhenAny is used and then the result task is awaited once again to get the id.
Running this gives:
Task2 after 3000ms - actual task
Finished one of the two : i2
Task1 after 5000ms - time constraint
Now change the second to last longer:
...
var t2 = e.DelayWithEcho( 7000, "Task2 po 7000ms - actual task",     "i2" ); 
...
and the result is
Task1 after 5000ms - time constraint
Finished one of the two : i1
Task2 after 7000ms - actual task