I want to create/initialize a Task object from a async Task method without starting it directly. When searching online I only find answer with Task created from void methods.
This is the task method I want to execute. As I need to do some web requests async, the method needs to be async. I want to do exception handling as well, so it can't be an async void
.
private async Task WebRequestTask()
{
try
{
string ResponseText = await httpClient.GetStringAsync("https://fakeurl.com");
// process response code
}
catch (Exception ex)
{
// handle error
}
}
This is my main method where I want to create the task:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
Task RequestTask = ... // here I want to initialize the task without starting
// chain continuation tasks to RequestTask
RequestTask.Start();
}
I've tried following solutions but nothing answers my need:
Solution 1
Task RequestTask = new Task(WebRequestTask);
Task RequestTask = WebRequestTask;
-> both cause a compiler error
Solution 2
Task RequestTask = Task.Run(WebRequestTask);
-> this start the task async and the current method continues (but here it could be possible an exception is thrown before the continuation tasks are chained)
Solution 3
Task RequestTask = WebRequestTask();
-> this start the task synchronously and chaining happens after the task is finished
Solutions 4
Task<Task> OuterTask = new Task<Task>(LoginToAzure);
await LoginAzureTask.Unwrap();
-> this start the outer task but the inner task is never called
How can I attach this Task method to an Task object, so that I can first attach continuation tasks/set some options and then start it? When it's possible, I'd like to use the cancellation token as well.
The asynchronous methods that are implemented with the async
keyword are creating "hot" Task
s, in other words Task
s that are already started. You can defer the execution of an async
method by wrapping it in another Task
, thus creating a nested Task<Task>
, like this:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
var token = TokenSource.Token;
Task<Task> taskTask = new Task<Task>(() => WebRequestAsync(token));
Task task = taskTask.Unwrap(); // The task is not started yet
// Chain continuations to task
taskTask.RunSynchronously(TaskScheduler.Default);
// The task is now started
}
private async Task WebRequestAsync(CancellationToken cancellationToken)
{
string responseText = await httpClient.GetStringAsync(
"https://fakeurl.com", cancellationToken);
// Process response text
}
The taskTask
variable is the wrapper that represents just the execution of the WebRequestAsync
method, not the completion of the Task
that this method creates. The wrapper task is completed immediately after calling the RunSynchronously
method, and after that point your code will have no use for it. The task
variable "unwraps" the wrapper, and represents the actual asynchronous web request. It is completed when the asynchronous operation completes.
As you can see this technique is rather cumbersome. You'll rarely see it in application code, because pure async-await composition is more convenient to use.