Под обложками у javascript есть очередь событий. Каждый раз, когда завершается поток выполнения javascript, он проверяет, есть ли еще одно событие в очереди для обработки. Если он есть, он вытаскивает его из очереди и вызывает это событие (например, щелчок мыши).
Сеть с собственным кодом, которая находится под вызовом ajax, будет знать, когда будет выполнен ответ ajax, и событие будет добавлено в очередь событий javascript. То, как нативный код знает, когда выполняется вызов ajax, зависит от реализации. Он может быть реализован с помощью потоков или может быть сам по себе управляемым событиями (это на самом деле не имеет значения). Суть реализации заключается в том, что когда ответ ajax завершен, некоторый нативный код узнает, что он выполнен, и поместит событие в очередь JS.
Если в данный момент не запущен Javascript, событие будет немедленно запущено, что запустит обработчик ответа ajax. Если что-то выполняется в то время, то событие будет обработано, когда закончится текущий поток выполнения javascript. Там не должно быть никакого опроса с помощью движка JavaScript. Когда часть Javascript завершает работу, движок JS просто проверяет очередь событий, чтобы увидеть, есть ли что-то еще, что нужно запустить. Если это так, он выталкивает следующее событие из очереди и выполняет его (вызывая одну или несколько функций обратного вызова, которые зарегистрированы для этого события). Если в очереди событий ничего нет, то у интерпретатора JS есть свободное время (сборка мусора или бездействие) до тех пор, пока какой-либо внешний агент не поместит что-то еще в очередь событий и не разбудит его.
Поскольку все внешние события проходят через очередь событий, и ни одно событие не запускается, пока javascript фактически выполняет что-то еще, он остается однопоточным.
Вот несколько статей о деталях: