Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I produce a Task<Task> to Unwrap

Can someone please explain the difference between these two statements:

Task<Task> bTask = backup.BackupCurrentDatabaseAsync()
    .ContinueWith(_ => CompressArchiveAsync());
//unwrap the tasks to produce one entire task
Task t = bTask.Unwrap();

vs

Task<Task> bTask = backup.BackupCurrentDatabaseAsync()
    .ContinueWith(_ => 
{
    CompressArchiveAsync();
});
//unwrap the tasks to produce one entire task
Task t = bTask.Unwrap();

The methodsExtractArchiveAsync(), BackupCurrentDatabaseAsync(), RestoreDatabaseAsync() all return a Task.

Here, the first Continuation returns a Task<Task>. I can then Unwrap() this task to put Continuations on the resultant (inner) task.

The second version doesn't compile. The only different here is the braces around the CompressArchiveAsync().

I am trying to access the resultant (internal) Task to check the Task.Status. If I use the second method, the Task.Status is reporting the result of the BackupCurrentDatabaseAsync() task.

like image 245
Simon Avatar asked Dec 17 '14 16:12

Simon


People also ask

What is task Unwrap?

Unwrap(Task<Task>) Creates a proxy Task that represents the asynchronous operation of a Task<Task> (C#) or Task (Of Task) (Visual Basic). Unwrap<TResult>(Task<Task<TResult>>) Creates a proxy Task that represents the asynchronous operation of a Task<Task<T>> (C#) or Task (Of Task(Of T)) (Visual Basic).

What is task factory StartNew?

StartNew(Action<Object>, Object, CancellationToken, TaskCreationOptions, TaskScheduler) Creates and starts a task for the specified action delegate, state, cancellation token, creation options and task scheduler.

What is nested task?

A nested task is just a Task instance that is created in the user delegate of another task. A child task is a nested task that is created with the AttachedToParent option. A task may create any number of child and/or nested tasks, limited only by system resources.


4 Answers

.ContinueWith(_ => CompressArchiveAsync());

is equivalent to:

.ContinueWith(_ => 
{
    return CompressArchiveAsync();
});

Notice the return.

Your second code snippet doesn't compile because ContinueWith doesn't return a Task<Task>, but simply a Task, and there's nothing to unwrap.

The following is bound to a Func<Task, Task> (a function that takes a Task and returns a Task)

_ => 
{
    return CompressArchiveAsync();
}

But the following is actually bound to an Action<Task> (a function that takes a Task but doesn't return anything):

_ => 
{
    CompressArchiveAsync();
}

And the reference to the Task created by CompressArchiveAsync is never returned. Without a reference to it, you can't check the Task's status.

Note that:

  • ContinueWith<TResult>(Func<Task, TResult>) returns a Task<TResult>
  • ContinueWith(Action<Task>) returns a Task.

Therefore your ContinueWith(Func<Task, Task>) returns a Task<Task> that you can unwrap, but your ContinueWith(Action<Task>) simply returns a Task.

like image 88
Lucas Trzesniewski Avatar answered Nov 04 '22 12:11

Lucas Trzesniewski


The difference is in the Lambda Expression syntax.

There are 2 types of Lambdas: Expression Lambdas and Statement Lambdas. Expression Lambdas have no braces and return the result of the expression while Statement Lambdas have braces containing zero or more statements (one of them can be a return statement).

So this Expression Lambda:

_ => CompressArchiveAsync()

Is equivalent to this Statement Lambda:

_ => { return CompressArchiveAsync(); }

So, the difference is that in the first continuation you are returning a task but in the second you are not, it's just a void anonymous delegate. That's why the first continuation is a Task<Task> while the second is just a Task.

like image 32
i3arnon Avatar answered Nov 04 '22 12:11

i3arnon


Long comment to show 4.5 code.

If you could move to .Net 4.5 than code you are trying to write can be rewritten in more compact way with async/await which is essentially implements all that code internally:

 async Task CompleteBackup()
 {
    await backup.BackupCurrentDatabaseAsync()
    await CompressArchiveAsync());
    await .....
 }
like image 2
Alexei Levenkov Avatar answered Nov 04 '22 11:11

Alexei Levenkov


In the first example, you are calling ContinueWith with a Func. Therefore, it will return a Task<T>. The second try will call the ContinueWith overload with an Action, because... well it's an action, it does not return anything. So it will return a simple Task without T.

like image 1
nvoigt Avatar answered Nov 04 '22 11:11

nvoigt