More complex, complete examples that do several things at once

You have to choose what you need and for what reasons. Interrupts are often required for precise timing. Non blocking code is required for optimal resource utilization. Depending on what you want to achieve you may have to do both. For non blocking code it is often a good idea to think about state engines. Here is an example that uses both a state engine AND interrupts for different purposes: Flexible Sweep | Blinkenlight.