Mere moments ago, quoth I:
Where your timer is being used as a timeout (so that you run them in parallel) things get more complex, since you have to deal with cancellation of the timer and/or the read (including coping with the case when you requested cancellation but it actually completed successfully before it could actually cancel; and not accidentally interpreting acknowledgment of cancelling your previous timer wait as a cancellation of a subsequent timer wait). But it should be doable.
Forgot to add: multiple operations concurrently in flight also means that you can't used the moved-handler lifetime model any more, and you'll probably need to switch to a shared-handler lifetime model instead. Or create separate sub-handler operation objects for each parallel action -- but in practice this will probably end up still requiring the original parent object to have a shared lifetime.