Как вы уже поняли и верно заметили, коллбэк это функция, которую передали куда-то и вызовут, когда надо будет.
Асинхронность достигается не за счёт коллбэков. Их просто удобно использовать там, где есть асинхронность. Хотя ещё удобнее промисы.
Асинхронность возникает там, где рвётся последовательный (строчка за строчкой) порядок выполнения кода. Где один код уже отыграл и отпустил движок JS – пусть остынет, нет пока больше дел. А остались навешанные слушатели событий, таймеры, нерешённые промисы, долгоиграющие ajax-запросы и прочая асинхрота.
В эти слушатели, таймауты и прочие и передают в коллбэке те строки кода, которые надо будет выполнить «потом».
Пример. Не хотите setTimeout, тогда с кнопкой пробела:
function superCallback() {
console.log("Свершилось! Коллбэк сработал.");
}
document.addEventListener("keydown", superCallback);
// на этот момент мы объявили функцию и повесили слушатель события
// дальше всё, делать больше нечего, движок JS свободен
// но слушатель сидит, ждёт.
Наступает
ночь асинхронность. Никакой код сейчас не работает. Но заряжен слушатель с коллбэком.
Теперь, если выждав паузу, нажать какую-нибудь клавишу, сработает слушатель и проиграет вслух код в коллбэке.