There are several problems with this code:
Delegate.Job1(request) .ContinueWith(Delegate.Job2) .ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted) .ContinueWith(Result);
First of all, you continue to execute using Delegate.Job2 , even if Delegate.Job1 fails. Therefore you need the value OnlyOnRanToCompletion . Like continuing the Result , you continue in all cases, so the task with the error still goes through the chain and, as you have already seen, is in the Faulted state with null as a result.
So, your code, if you cannot use await at this level, may be like this (just as @Evk pointed out, you had to add exception handling to all your code, which is really ugly):
Delegate.Job1(request) .ContinueWith(Delegate.Job2, TaskContinuationOptions.OnlyOnRanToCompletion) .ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted) .ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion) .ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted);
However, you still have the option to use the await keyword inside your method, and after that use lambda to run it synchronously, if this is an option for you:
public async Task DoSomethingAsync(MyRequest request) { try { request.result1 = await delegateInstance.Job1(request); request.result2 = await delegateInstance.Job2(request); Console.Writeline(request.result1 + " " + request.result2); return result; } catch(Exception e) { } } public void ICannotBeAsync() { var task = Task.Run(() => caller.DoSomethingAsync(request);
Exception handling can be performed at all levels, so you need to submit it. If something goes wrong at runtime, Result will raise an AggregateException as a wrapper for internal exceptions that occurred during the call. You can also use the Wait method for a task wrapped in a try/catch clause, and then check the status of the task and process it as necessary (it has IsFaulted , IsCompleted , IsCanceled boolean properties).
In addition, it is strongly recommended that you use some undo logic for task-oriented tasks to undo unnecessary work. You can start with this MSDN article .
Update on other issues:
If you still want to use ContinueWith instead of await and want to change the method signatures of Job1 , Job2 , you must change your code as follows:
Delegate.Job1(request) .ContinueWith(_ => Delegate.Job2(request), TaskContinuationOptions.OnlyOnRanToCompletion) .ContinueWith(Result, TaskContinuationOptions.OnlyOnRanToCompletion) .ContinueWith(Fault, TaskContinuationOptions.OnlyOnFaulted);
The reason for this is that the ContinueWith method accepts Func<Task, Task> , because you need to, in general, check the task for status and / or result.
Regarding the issue of not blocking the caller, you can try the TaskCompletionSource<TResult> class, something like this:
public void ICannotBeAsync() { var source = new TaskCompletionSource<TResult>(); var task = Task.Run(() => caller.DoSomethingAsync(request, source); while (!source.IsCompleted && !source.IsFaulted) {