Тут могут быть разные варианты. Но чаще всего встречается "событийно ориентированное программирование".
В упрощенном виде это выглядит так:
while (true) //event loop
{
if (queue.empty())
continue;
auto event = queue.front();
queue.pop_front();
process(event);
}
onButton()
{
...
}
onTimer()
{
...
}
process(Event event)
{
if (isTimetEvent(event))
onTimer();
if (isButtonEvent(event))
onButton();
}
Основную часть времени программа крутится в вечном цикле. Но когда происходя события(events), они добавляются в очередь(queue) и вызываются их обработчики. Срабатывание таймера тоже становится событием.
Фишка в том, что обработчики таких событий не могут работать долго, иначе остальные будут заблокированы. Например плохая идея ждать в обработчике пользовательского ввода или делать большие вычисления. Для решения этих проблем используют потоки и другие техники.
В коде я не описал добавление событий в очередь. Это может происходить разными способами. Но в простейшем случае мы можем на каждой итерации опрашивать таймеры кнопки и другие источники о произошедших событиях.