Create continuations using the Task Parallel Library that can run immediately after the execution of the antecedent is complete or to chain tasks as continuations A continuation is like a delegate that can be attached to a task so that you can execute it once the task has finished its execution. When working with asynchronous programming in .Net, you might often need to chain tasks as continuations or pass data to a task based on the data returned by another task. To achieve this, you would need to leverage continuations. Continuations in the Task Parallel Library Before TPL was around, continuations were performed using callback methods. A continuation is just another task and doesn’t block the thread on which it is called. While continuation in TPL is simple to implement, here’s a quick look at the benefits that task continuations have to offer: Invoke one or more continuations using the same antecedent Invoke a continuation when one or more antecedents have completed their execution Specify certain conditions based on which the continuations should be executed Chain continuations and pass data from an antecedent task to a continuation task Note that the ContinueWith method returns a task instance allowing you to have a chain of ContinueWith calls. Also, unlike the await keyword, the ContinueWith method doesn’t preserve the state and is executed by the default thread scheduler (if you don’t provide one) although you can also specify your custom scheduler when invoking the ContinueWith method. Creating continuation tasks using ContinueWith method You can create continuation tasks in TPL using the ContinueWith method on a Task. It should be noted that this method accepts just one parameter — the task that needs to be executed when the antecedent has completed its execution. The task instance on which the ContinueWith method is invoked is known as the antecedent task. Instead, the task that is executed by the ContinueWith method after execution of its antecedent task is complete is known as the continuation task. Let’s look at an example. Here’s how the syntax of the ContinueWith method looks: public Task<TResult> ContinueWith<TResult>( Func<Task, TResult> continuationFunction ) The ContinueWith method in turn returns another continuation task. Here’s a code snippet that illustrates how you can create a continuation task using one antecedent: public static void Main() { Task<string> initialTask = Task.Run(() => DateTime.Today.ToShortDateString()); Task continuation = initialTask.ContinueWith(antecedentTask => Console.WriteLine("Today is {0}.", antecedentTask.Result)); Console.Read(); } Refer to the code snippet given above. The first task is executed using a call to the Task.Run method. This is the antecedent task in our example. Once the execution of the antecedent task is complete, the continuation task is executed in the next statement. Note that you can also create a continuation that is to be executed when one or more antecedents have completed their execution. To execute a continuation when all the antecedent tasks are complete, you can take advantage of the Task.WhenAll or the TaskFactory.ContinueWhenAll methods. Alternatively, you can execute a continuation when any of the antecedent tasks are complete using the Task.WhenAny or TaskFactory.ContinueWhenAny methods. It should be noted that none of these methods block the calling thread. Although there are several methods (ie., Wait, WaitAll, WaitAny, Result, and GetAwaiter().GetResult()) that allow you to wait till the execution of a task is complete, all of these synchronously block the calling thread. You can also specify continuation options when using the ContinueWith, ContinueWhenAll methods. To do this, you would need to take advantage of the Task.TaskContinuationOptions enumeration of the System.Threading namespace. As an example, you can ensure that a continuation executes only after the execution of the antecedent has completed its execution. var continuationTask = task.ContinueWith( (antecedentTask) => { Console.WriteLine("This will execute only after the antecedent has completed its execution."); } , TaskContinuationOptions.OnlyOnRanToCompletion); Here’s another example that illustrates how task continuation options can be used to execute a continuation only after the antecedent has completed its execution. public static void Main() { var task = Task.Run(() => { return "Joydip"; }); var t = task.ContinueWith((antecedent) => { Console.WriteLine("Hello {0}", antecedent.Result); }, TaskContinuationOptions.OnlyOnRanToCompletion); Console.Read(); } If you use ContinueWith without mentioning any special flag, the continuation will always be executed regardless of the state of the antecedent, i.e., the continuation will be executed even if the antecedent has completed its executed in a faulted or a cancelled state. As a side note, let me tell you that it is advisable to avoid using ContinueWith in your applications. In doing so, you can avoid threading issues and also avoid allocating an extra task instance when it is not needed in your application. You can do away from ContinueWith by using task helpers. I will discuss more on this in a later post here. Related content feature 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos