I did a many-thousand line system this way and it ran very smoothly. For me the trick was when to allow timeouts and when to block them due to shared resources/data structures. In my case, I seldom used actual timeouts for swithching between functions. I wrote subs that ran for a very short amount of time that were triggered by internal or external events. The timers mostly served to cause screen synchronization with actual data, update the status of other nodes (in a cluster) and prevent the system from hanging when, say, an expected event such as a user input took some time.
One interesting thing to consider: if you are displaying a large amount of data, consider how often it must be updated and only compute values when they need to be displayed. That is, if you ahve a rapidly changing value for $foo, don't compute the median $foo until you need to show it (unless, of course, you need to do something else with it). This saves time and hence makes the system more responsive.