Как в языках программирования реализуется асинхронное поведение?
Есть множество способов реализации: потоки ОС, сопрограммы (корутины), событийный подход.
Конекретно JavaScript -- однопоточный, и в нём используется последний способ. При вызове setInterval/setTimeout внутри js движка создаётся объект, связывающий таймер и коллбэк (обработчик события). Каждый тик движок проверяет не истёк ли таймер и если истёк, то добавляет коллбэк в очередь вызовов. XMLHTTPRequest работает аналогично, только вместо истечения интервала "ожидает" ответ от сервера.
Полезные ссылки:setTimeout и setIntervalРазличия асинхронной и многопоточной архитектуры н...How JavaScript Timers Work
В JS данное поведение реализовано через Event Loop. Это механизм, который позволяет связывать события (истечение таймера, ответ на запрос, явно вызванное событие) и функцию-обработчик, которая вызывается после того, как это событие наступило. Жирным выделенное означает, что функция вызовется не в сразу же после наступления события, но в произвольный момент времени после. Т.е. она не будет вызвана раньше, чем случится событие, но когда именно она будет вызвана после его наступления – зависит от того, когда Event Loop снова получит управление, т.е. отсутствует понятие прерывания – обработчик не будет вызван, пока выполняется какая-либо другая функция. Соответственно, как только в Event Loop'e больше нет выполняющихся либо ожидающих функций, программа завершается.setTimeout(function() { console.log('Timeout 1000'); }, 1000 );
setTimeout(function() { console.log('Timeout 2000'); }, 2000);

console.log('Now!');
Выведет Now!
Timeout 1000
Timeout 2000
И завершится, хотя программа достигла конца после вывода "Now!"