Почему рекурсия не останавливается, если я заключу foo++ в setTimeout? Я почти уверен, что упускаю из виду важную концепцию JavaScript, касающуюся асинхронных операций.

let foo = 0;

const bar = () => {
  setTimeout(() => foo++);

  if (foo <= 2) {
    bar();
  }
}

bar();
1
Vinay Sharma 21 Янв 2021 в 13:01

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 () из стека, он продолжает расти, давая вам ошибку «Превышен максимальный размер стека вызовов».

6
Nick Parsons 21 Янв 2021 в 10:47

Вы должны поместить рекурсию в обратный вызов setTimeout:

let foo = 0;

const bar = () => {foo++; if (foo <= 2) setTimeout(bar);};

bar();

console.log(foo); // Displays 3
0
MetallimaX 21 Янв 2021 в 10:49
65825048