The new async/await pattern in C# is a great step forward but when dealing with asynchronous computations or more generally processes, three things inevitably show up:
- progress reporting
- cancellation
- multi-valued returns
There's an article about how to incorporate both cancellation and progress reporting into asynchronous operations but at that point algorithms start to become less elegant in my oppinion.
If we add multi-valued returns into the mix the only real alternative in town appears to be Rx.
Should we use Rx, cancellation is more elegant than with async and TaskCancellationToken; we can just call Dispose() on the subscription.
But how to deal with progress reporting?
We can simply model progress as an event too. In a language that supports discriminated unions like F# this is trivial but it's of course also possible to model in C# etc.
An Rx event stream can be used for single-value return functions as well (and a zero-returning function can be treated as an error case) but async doesn't appear so well suited for representing multi-valued returns because the returns come back in one chunk, unless you use something like IAsyncEnumerable.
But where does async really excel over Rx (complementary as they may be)? Language support! Async/await is a language feature whereas Rx is not. That is, until someone came up with the idea of using F# workflow builders. This allows one to add custom semantics to existing syntactic constructs like for, while, use etc.
I do wonder, though, how a language where the designer had all these things in mind from the outset, could look because I still feel like asynchrony has not been fully embraced yet.
Here's an alternative way to deal with asynchrony and progress reporting which plugs nicely into any GUI application (in this case a side-effecting (IO) operation where we are not really interested in values but progress reporting):
process
|> Rx.map
(fun signal ->
match signal with
| ValueSignal x ->
()
| ErrorSignal e ->
()
| StatusSignal s ->
processStatusLabel.Text <- s
| ProgressSignal p ->
processProgressBar.Value <- p
signal)
|> Rx.subscribe
|> ignore
In this model a "process" is a signal of signals, where we can pattern match over the aspects we are interested in.
The Rx approach incidentally is also great for hooking up logging and other listeners as a separate aspect.
What do you think about this whole problematic, where .NET and WinRT is heading and the state of language support today?
Add your 2¢