The issue is that once you start doing serious work with asynchrony -- filtering streams, handling disconnects, backing out of parse errors, and dealing with parallelism all at the same time -- then being able to reason clearly about the control flow is paramount to getting anything done.
If sometimes code below the callback registration will be executed first, and sometimes not, depending on unpredictable runtime state, it's game over. Even if you understand the code and it works now, good luck when a dependency update introduces a new corner case and your app's control flow has been radically changed and you're stuck midnight-debugging the mess.
I guess this is one of the things you learn in the trenches, along with avoiding global variables and not optimizing prematurely.
Trying to accommodate the complexity you describe ahead of time could well be its own mistake. One thing I've learned in the trenches is to wait for most things to prove to be a problem before adding complexity to address them. I believe you that there are cases like you and the OP describe, where "all sync or all async" is a good invariant to have. But I don't believe it's a general rule, the alternative to which is doing it wrong (or even the devil, as in the OP).
Another thing one learns in the trenches is that almost all general rules about software are bogus and end up distorting one's thinking. It's better to choose invariants on a system-by-system basis.
In your use case it's possible you'll never hit that failure scenario.