Note

Access to this page requires authorization. You can try signing in or .

Access to this page requires authorization. You can try .

How to: Unwrap a Nested Task

You can return a task from a method, and then wait on or continue from that task, as shown in the following example:

static Task<string> DoWorkAsync() => Task<string>.Factory.StartNew(() =>
 {
 //...
 return "Work completed.";
 });

static void StartTask()
{
 Task<string> t = DoWorkAsync();
 t.Wait();
 Console.WriteLine(t.Result);
}
Shared Function DoWorkAsync() As Task(Of String)

 Return Task(Of String).Run(Function()
 '...
 Return "Work completed."
 End Function)
End Function

Shared Sub StartTask()

 Dim t As Task(Of String) = DoWorkAsync()
 t.Wait()
 Console.WriteLine(t.Result)
End Sub

In the previous example, the Result property is of type string (String in Visual Basic).

However, in some scenarios, you might want to create a task within another task, and then return the nested task. In this case, the TResult of the enclosing task is itself a task. In the following example, the Result property is a Task<Task<string>> in C# or Task(Of Task(Of String)) in Visual Basic.

// Note the type of t and t2.
Task<Task<string>> t = Task.Factory.StartNew(DoWorkAsync);
Task<Task<string>> t2 = DoWorkAsync().ContinueWith((s) => DoMoreWorkAsync());

// Outputs: System.Threading.Tasks.Task`1[System.String]
Console.WriteLine(t.Result);
' Note the type of t and t2.
Dim t As Task(Of Task(Of String)) = Task.Run(Function() DoWorkAsync())
Dim t2 As Task(Of Task(Of String)) = DoWorkAsync().ContinueWith(Function(s) DoMoreWorkAsync())

' Outputs: System.Threading.Tasks.Task`1[System.String]
Console.WriteLine(t.Result)

Although it is possible to write code to unwrap the outer task and retrieve the original task and its Result property, such code is not easy to write because you must handle exceptions and also cancellation requests. In this situation, we recommend that you use one of the Unwrap extension methods, as shown in the following example.

// Unwrap the inner task.
Task<string> t3 = DoWorkAsync().ContinueWith((s) => DoMoreWorkAsync()).Unwrap();

// Outputs "More work completed."
Console.WriteLine(t.Result);
' Unwrap the inner task.
Dim t3 As Task(Of String) = DoWorkAsync().ContinueWith(Function(s) DoMoreWorkAsync()).Unwrap()

' Outputs "More work completed."
Console.WriteLine(t.Result)

The Unwrap methods can be used to transform any Task<Task> or Task<Task<TResult>> (Task(Of Task) or Task(Of Task(Of TResult)) in Visual Basic) to a Task or Task<TResult> (Task(Of TResult) in Visual Basic). The new task fully represents the inner nested task, and includes cancellation state and all exceptions.

Example

The following example demonstrates how to use the Unwrap extension methods.


namespace Unwrap
{
 using System;
 using System.Threading;
 using System.Threading.Tasks;

 class Program
 {
 static void Main()
 {
 // An arbitrary threshold value.
 byte threshold = 0x40;

 // data is a Task<byte[]>
 Task<byte[]> data = Task<byte[]>.Factory.StartNew(GetData);

 // We want to return a task so that we can
 // continue from it later in the program.
 // Without Unwrap: stepTwo is a Task<Task<byte[]>>
 // With Unwrap: stepTwo is a Task<byte[]>
 Task<byte> stepTwo = data.ContinueWith((antecedent) =>
 {
 return Task<byte>.Factory.StartNew(() => Compute(antecedent.Result));
 })
 .Unwrap();

 // Without Unwrap: antecedent.Result = Task<byte>
 // and the following method will not compile.
 // With Unwrap: antecedent.Result = byte and
 // we can work directly with the result of the Compute method.
 Task<Task> lastStep = stepTwo.ContinueWith((antecedent) =>
 {
 if (antecedent.Result >= threshold)
 {
 return Task.Factory.StartNew(() =>
 Console.WriteLine($"Program complete. Final = 0x{stepTwo.Result:x} threshold = 0x{threshold:x}"));
 }
 else
 {
 return DoSomeOtherAsynchronousWork(stepTwo.Result, threshold);
 }
 });

 lastStep.Wait();
 Console.WriteLine("Press any key");
 Console.ReadKey();
 }

 #region Dummy_Methods
 private static byte[] GetData()
 {
 Random rand = new();
 byte[] bytes = new byte[64];
 rand.NextBytes(bytes);
 return bytes;
 }

 static Task DoSomeOtherAsynchronousWork(int i, byte b2)
 {
 return Task.Factory.StartNew(() =>
 {
 Thread.SpinWait(500000);
 Console.WriteLine("Doing more work. Value was <= threshold");
 });
 }

 static byte Compute(byte[] data)
 {
 byte final = 0;
 foreach (byte item in data)
 {
 final ^= item;
 Console.WriteLine($"{final:x}");
 }
 Console.WriteLine("Done computing");
 return final;
 }
 #endregion
 }
}
'How to: Unwrap a Task
Imports System.Threading
Imports System.Threading.Tasks

Module UnwrapATask2

 Sub Main()
 ' An arbitrary threshold value.
 Dim threshold As Byte = &H40

 ' myData is a Task(Of Byte())

 Dim myData As Task(Of Byte()) = Task.Factory.StartNew(Function()
 Return GetData()
 End Function)
 ' We want to return a task so that we can
 ' continue from it later in the program.
 ' Without Unwrap: stepTwo is a Task(Of Task(Of Byte))
 ' With Unwrap: stepTwo is a Task(Of Byte)

 Dim stepTwo = myData.ContinueWith(Function(antecedent)
 Return Task.Factory.StartNew(Function()
 Return Compute(antecedent.Result)
 End Function)
 End Function).Unwrap()

 Dim lastStep = stepTwo.ContinueWith(Function(antecedent)
 Console.WriteLine("Result = {0}", antecedent.Result)
 If antecedent.Result >= threshold Then
 Return Task.Factory.StartNew(Sub()
 Console.WriteLine("Program complete. Final = &H{1:x} threshold = &H{1:x}",
 stepTwo.Result, threshold)
 End Sub)
 Else
 Return DoSomeOtherAsynchronousWork(stepTwo.Result, threshold)
 End If
 End Function)
 Try
 lastStep.Wait()
 Catch ae As AggregateException
 For Each ex As Exception In ae.InnerExceptions
 Console.WriteLine(ex.Message & ex.StackTrace & ex.GetBaseException.ToString())
 Next
 End Try

 Console.WriteLine("Press any key")
 Console.ReadKey()
 End Sub

#Region "Dummy_Methods"
 Function GetData() As Byte()
 Dim rand As Random = New Random()
 Dim bytes(64) As Byte
 rand.NextBytes(bytes)
 Return bytes
 End Function

 Function DoSomeOtherAsynchronousWork(ByVal i As Integer, ByVal b2 As Byte) As Task
 Return Task.Factory.StartNew(Sub()
 Thread.SpinWait(500000)
 Console.WriteLine("Doing more work. Value was <= threshold.")
 End Sub)
 End Function

 Function Compute(ByVal d As Byte()) As Byte
 Dim final As Byte = 0
 For Each item As Byte In d
 final = final Xor item
 Console.WriteLine("{0:x}", final)
 Next
 Console.WriteLine("Done computing")
 Return final
 End Function
#End Region
End Module

See also


Feedback

Was this page helpful?

Additional resources