Почему рекурсия не останавливается, если я заключу foo++
в setTimeout
? Я почти уверен, что упускаю из виду важную концепцию JavaScript, касающуюся асинхронных операций.
let foo = 0;
const bar = () => {
setTimeout(() => foo++);
if (foo <= 2) {
bar();
}
}
bar();
2 ответа
Когда вы вызываете bar()
, он добавляется к так называемому стеку вызовов < / а>. Стек вызовов используется для отслеживания того, где мы находимся в нашем скрипте, когда мы вызываем и возвращаемся из функций. Когда функция вызывается, она добавляется в стек вызовов, а при возврате извлекается из стека вызовов.
Stack:
- bar()
Когда bar()
запускается, он вызывает setTimeout()
, который добавляется в стек вызовов.
Функция setTimeout()
запускает веб-API и завершает / возвращает его, выталкивая его из стека вызовов. Затем веб-API ожидает 0 мс (0 мс, когда в setTimeout не передается задержка, по умолчанию он равен 0) и помещает / помещает ваш обратный вызов () => foo++
в то, что называется очередью задач.
Task queue: (front ---- back)
() => foo++
Задачи в очереди задач извлекаются / удаляются из очереди с помощью цикла событий только тогда, когда стек вызовов пуст. Это важно, поскольку это означает, что вышеупомянутый обратный вызов, который увеличивает foo
, будет вызываться только после возврата bar()
(таким образом выталкивая его из стека вызовов), однако этого никогда не происходит, поскольку {{X2} } продолжает вызывать себя непрерывно, так как ваше условие if всегда будет истинным и, как следствие, продолжит добавлять bar()
в стек вызовов.
Stack:
- bar() // after first recursive call
- bar()
По мере того как вы продолжаете вызывать bar()
в своих рекурсивных функциях, ваш стек вызовов начинает заполняться, как и ваша очередь задач:
Stack:
- bar() // after N recursive calls
...
- bar()
- bar()
Поскольку ваш стек вызовов никогда не имеет возможности вывести bar () из стека, он продолжает расти, давая вам ошибку «Превышен максимальный размер стека вызовов».
Вы должны поместить рекурсию в обратный вызов setTimeout:
let foo = 0;
const bar = () => {foo++; if (foo <= 2) setTimeout(bar);};
bar();
console.log(foo); // Displays 3
Похожие вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript / JS) и его различных диалектах / реализациях (кроме ActionScript). Включите все соответствующие теги в свой вопрос; например, [node.js], [jquery], [json] и т. д.