Цепные вызовы setTimeout на самом деле не являются "рекурсивными". Если одна функция setTimeout, при вызове, устанавливает другой тайм-аут с самим собой в качестве обработчика, исходный вызов будет долго уйти к тому времени, когда наступит новый таймаут.
Использование setTimeout вместо setInterval (для меня) часто более гибко, поскольку позволяет самому временному коду адаптироваться (и, возможно, отменяться). Общий шаблон заключается в том, чтобы установить замыкание вокруг вызова setTimout так, чтобы данные, необходимые для синхронизированного процесса (анимация, если это то, что вы делаете), были доступны и изолированы от остальной части приложения. Затем тайм-аут запускается при первом запуске.
Внутри обработчика времени ожидания «arguments.callee» может использоваться для обращения к себе и сброса времени ожидания для последующих «кадров».