I don’t think this proves what you think it proves.
Many of the problems I see here that present intermittently, including “only affects some devices” turn out to be race conditions, so I would suggest carefully auditing your code for those. Look at any time you modify any external state (including but not limited to controller properties) from within a
then block or
subscribe handler (or a part of an RxJS pipeline, but you aren’t modifying state at all from inside there, right?). Now examine every place where that state is relied upon - will it behave sanely both before and after the future resolves, whenever that is?
I suggest roughly dividing all asynchronous code into two types: those that do stuff and those that generate values. Functions that do stuff should be as self-contained as possible, and functions that generate values should always return a future. Callers of those functions should virtually never be ignoring the return value - doing so is a strong indicator that you’re relying on out-of-band state transfer, which is a breeding ground for difficult-to-reproduce bugs.