Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between await and ContinueWith

Can someone explain if await and ContinueWith are synonymous or not in the following example. I'm trying to use TPL for the first time and have been reading all the documentation, but don't understand the difference.

Await:

String webText = await getWebPage(uri); await parseData(webText); 

ContinueWith:

Task<String> webText = new Task<String>(() => getWebPage(uri)); Task continue = webText.ContinueWith((task) =>  parseData(task.Result)); webText.Start(); continue.Wait(); 

Is one preferred over the other in particular situations?

like image 500
Harrison Avatar asked Sep 23 '13 17:09

Harrison


People also ask

When should I use task ContinueWith?

The ContinueWith function is a method available on the task that allows executing code after the task has finished execution. In simple words it allows continuation. Things to note here is that ContinueWith also returns one Task. That means you can attach ContinueWith one task returned by this method.

What does calling task ContinueWith () do?

ContinueWith(Action<Task>)Creates a continuation that executes asynchronously when the target Task completes.

What is the difference between Sync and await?

The differences between asynchronous and synchronous include: Async is multi-thread, which means operations or programs can run in parallel. Sync is single-thread, so only one operation or program will run at a time. Async is non-blocking, which means it will send multiple requests to a server.

Is await a sync or async?

Await is in an async function to ensure that all promises that are returned in the function are synchronized. With async/await, there's no use of callbacks.


1 Answers

Here's the sequence of code snippets I recently used to illustrate the difference and various problems using async solves.

Suppose you have some event handler in your GUI-based application that takes a lot of time, and so you'd like to make it asynchronous. Here's the synchronous logic you start with:

while (true) {     string result = LoadNextItem().Result;     if (result.Contains("target")) {         Counter.Value = result.Length;         break;     } } 

LoadNextItem returns a Task, that will eventually produce some result you'd like to inspect. If the current result is the one you're looking for, you update the value of some counter on the UI, and return from the method. Otherwise, you continue processing more items from LoadNextItem.

First idea for the asynchronous version: just use continuations! And let's ignore the looping part for the time being. I mean, what could possibly go wrong?

return LoadNextItem().ContinueWith(t => {     string result = t.Result;     if (result.Contains("target")) {         Counter.Value = result.Length;     } }); 

Great, now we have a method that does not block! It crashes instead. Any updates to UI controls should happen on the UI thread, so you will need to account for that. Thankfully, there's an option to specify how continuations should be scheduled, and there's a default one for just this:

return LoadNextItem().ContinueWith(t => {     string result = t.Result;     if (result.Contains("target")) {         Counter.Value = result.Length;     } }, TaskScheduler.FromCurrentSynchronizationContext()); 

Great, now we have a method that does not crash! It fails silently instead. Continuations are separate tasks themselves, with their status not tied to that of the antecedent task. So even if LoadNextItem faults, the caller will only see a task that has successfully completed. Okay, then just pass on the exception, if there is one:

return LoadNextItem().ContinueWith(t => {     if (t.Exception != null) {         throw t.Exception.InnerException;     }     string result = t.Result;     if (result.Contains("target")) {         Counter.Value = result.Length;     } }, TaskScheduler.FromCurrentSynchronizationContext()); 

Great, now this actually works. For a single item. Now, how about that looping. Turns out, a solution equivalent to the logic of the original synchronous version will look something like this:

Task AsyncLoop() {     return AsyncLoopTask().ContinueWith(t =>         Counter.Value = t.Result,         TaskScheduler.FromCurrentSynchronizationContext()); } Task<int> AsyncLoopTask() {     var tcs = new TaskCompletionSource<int>();     DoIteration(tcs);     return tcs.Task; } void DoIteration(TaskCompletionSource<int> tcs) {     LoadNextItem().ContinueWith(t => {         if (t.Exception != null) {             tcs.TrySetException(t.Exception.InnerException);         } else if (t.Result.Contains("target")) {             tcs.TrySetResult(t.Result.Length);         } else {             DoIteration(tcs);         }}); } 

Or, instead of all of the above, you can use async to do the same thing:

async Task AsyncLoop() {     while (true) {         string result = await LoadNextItem();         if (result.Contains("target")) {             Counter.Value = result.Length;             break;         }     } } 

That's a lot nicer now, isn't it?

like image 146
pkt Avatar answered Nov 02 '22 02:11

pkt