Чтобы дополнить ответ Квентина, эта строка setTimeout(go, 1000)
(FYI, setTimeout
- правильное написание, а не setTimeOut
) на самом деле не выполняет никакой рекурсии. Он передает функцию go
, вызываемую через 1000 мс в событии l oop после опустошения стека (тот же стек, который может выполнять рекурсию и переполнение). Случайно, что все это происходит в той же функции, которая передается в качестве параметра тайм-ауту, что делает его визуально рекурсивным.
Происходит то, что строка setTimeout
запускается и добавляет функцию go
к событию l oop и гарантирует задержку не менее 1000 мс. Затем выполняется остальная часть синхронного кода, включая остальную часть go
и current++;
. Позже, когда стек пуст, задачи на l oop выполняются по порядку, включая go
(при условии, что таймер 1000 мс истек).
Это объясняет, почему такой код, как:
(function run() {
requestAnimationFrame(run);
// do stuff
})();
никогда не переполняет стек: на самом деле это не рекурсия и каждый кадр вызова уничтожается до того, как произойдет следующий вызов.
В стороне, может показаться удивительным, что
(function run() {
requestAnimationFrame(run);
// do stuff
})();
и
(function run() {
// do stuff
requestAnimationFrame(run);
})();
ведут себя примерно одинаково. Причина в том, что следующий обратный вызов run
гарантированно выполнится после того, как весь синхронный код (текущий вызов run
и все остальное в стеке вызовов) завершит выполнение, поэтому это не похоже на то, что местоположение вызывает блок в синхронном исполнении и дочерний вызов выполняет работу, как в случае с рекурсией.