Stephen Cleary

Thanks for the video, guys! Queues and separation of backend processing is an important subject that a lot of developers don't see the need for, and I'm sure these videos will be helpful!

I do have a few corrections regarding the async/await usage (mainly talking to others who are going to be watching this video):

First, keep in mind that the threading situation is very different in the front-end (WebAPI) in the Post method, and in the back-end (worker role). In WebAPI, you're in ASP.NET; whereas the worker role is just a single thread.

My comments:- An async WebAPI method should return Task or Task<T>, not void. If you actually call this code, ASP.NET will catch this mistake and throw an InvalidOperationException with the rather confusing error message "An asynchronous module or handler completed while an asynchronous operation was still pending." The fix is to make Post into an async Task method instead of async void.- It's not actually "fire and forget". ASP.NET keeps track of all asynchronous methods that have not yet completed. So, even though your code doesn't have to handle it, ASP.NET does. To be clear, "fire and forget" is extremely dangerous on ASP.NET and most server scenarios.- In the ASP.NET threading model, you don't want to call Wait on a Task. This will immediately negate all the scalability benefits of async/await, among other problems. If you need to apply a timeout, use cancellation, as such:

- In the worker role thread, you can use Wait. Although, the code "queue.CreateIfNotExistsAsync().Wait()" is not doing anything more than "queue.CreateIfNotExists()" would.- The video made a good point that you can't make a worker role Run method async. This is a common mistake.- When you create a new asynchronous method (e.g., DoStuff), you should default the return type to Task or Task<T>, not void. Async void methods cause lots of problems.- Task.Factory.StartNew does not understand async methods, so the LongRunning option actually doesn't do anything. It's better to just use Task.Run, which was designed for use with async methods.

I enjoyed the session, but I must disagree with your assertion that async/await is more complex than callbacks.

First, there is an exception in the Task: Task.Exception. Conceptually, the Task<T> type can represent either success or error; it has both a Result and Exception property, which are roughly analogous to the two parameters for your callback.

Secondly, an implementation of IProgress<T> is already provided (Progress<T>), which you can instantiate with a delegate as such: "new Progress<int>(value => { ... })". In an MVVM app there's no need to create your own.

Those are the only real drawbacks to async/await that you mentioned. However, async/await also has several advantages over callbacks:

You mentioned one drawback to chains of asynchronous operations: the lambda nesting can get unwieldy. Await avoids this completely.

Another thing you mentioned is that it can be easy to accidentally ignore exceptions in callback code. The Task<T> type prevents this by encapsulating the result.

Await and Progress<T> both use SynchronizationContext automatically in an intelligent way. This makes any kind of thread marshaling or dispatcher service completely unnecessary, cleaning up service/VM code.

Error handling is easier. With callbacks, exceptions must be propagated explicitly. Await will naturally propagate them, keeping the original exception type and call stack even if the thread context changed. This is quite difficult to do yourself with portable code.

In particular, thanks for not just showing "what works" but also the pitfalls.

Just a couple of notes:

WhenAll will actually return the results of those tasks. So you can replace: await Task.WhenAll(results); return results.Select(t => t.Result);with: return await Task.WhenAll(results);

I recommend that async/await users never use Task.Result (for the reasons you mentioned). There's a bit of overhead to use "await task" instead of "task.Result" on a completed Task, but the code is more obviously correct.