I would say that TPL Dataflow covers specialized subset of functionality in Rx. Dataflow is for data processing which can take measurable amount of time, whereas Rx is for events, such as mouse position, error states, etc where handling time is negligible.
Example: your "subscribe" handler is asynchronous and you want no more than 1 executor at the time. With Rx you have to block, there is no other way around it, because Rx is async-agnostic and does not threat async in a special way in many places.
.Subscribe(myAsyncHandler().Result)
If you don't block, then Rx will consider that action is complete while handler is still being executed asynchronously.
You might think that if you do
.ObserveOn(Scheduler.EventLoopSchedule)
than problem is solved. But this will break your .Complete() workflow, because Rx will think that it is done as soon as it schedule execution and you will quit your application without waiting for async operation completion.
If you want to allow no more than 4 concurrent async tasks than Rx does not offer anything out of the box. Perhaps you can hack something by implementing your own scheduler, buffer, etc.
TPL Dataflow offer very nice solution in ActionBlock. It can throttle simultaneous actions to certain number and it does understand async operations, so calling Complete() and awaiting for Completed will do exactly what you would expect: waiting for all in-progress async tasks to complete.
Another feature TPL has is "backpressure". Let's say you discovered an error in your handling routine and need to recalc last month data. If you subscribe to your source using Rx, and your pipeline contains unbounded buffers, or ObserveOn, than you will run out of memory in matter of seconds because source will keep reading faster than processing can handle. Even if you implement blocking consumer, your source may suffer from blocking calls, for example if source is asynchronous. In TPL you can implement source as
while(...)
await actionBlock.SendAsync(msg)
which does not block source yet will await while handler is overloaded.
Overall, I found that Rx is good fit for actions which are time and computationally light. If processing time becomes substantial, you are in the world of strange side effects and esoteric debugging.
Good news is that TPL Dataflow blocks play very nice with Rx. They have AsObserver/AsObservable adapters and you can stick them in the middle of Rx pipeline when needed. But Rx has much more patterns and use cases. So my rule of thumb is to start with Rx and add TPL Dataflow as needed.