JavaScript. Когда именно стек вызовов становится "пустым"?
Я прочитал несколько сообщений /SO потоков в цикле событий и в соответствии с статья MDN,
Когда стек пуст, сообщение выгружается из очереди и обрабатываются.
Как начинающий JS, я все еще смущен - когда именно стек вызовов становится "пустым"? Например,
<script>
function f() {
console.log("foo");
setTimeout(g, 0);
console.log("foo again");
}
function g() {
console.log("bar");
}
function b() {
console.log("bye");
}
f();
/*<---- Is the stack empty here? */
b();
</script>
Правильный порядок выполнения foo
- foo again
- bye
- bar
.
Но сегодня я начал думать: не является ли стек технически пустым сразу после выхода из вызова f()
? Я имею в виду, что в этот момент мы не находимся внутри какой-либо функции, и мы не начали никакого нового выполнения, поэтому не следует обрабатывать сообщение о вызове setTimeout
(которое было немедленно поставлено в очередь), прежде чем перейти к b()
, и задавая порядок foo
- foo again
- bar
- bye
?
Что делать, если миллион строк кода или какое-то интенсивное вычисление, которое должно быть выполнено, и setTimeout(func, 0)
просто сидит в очереди, однако долго?
Ответы
Ответ 1
Хотя блок кода в тегах <script>
не заключен в явную функцию, может быть полезно подумать об этом как о глобальной функции, которую браузер сообщает о запуске javascript. Таким образом, стек вызовов не пуст, пока не будет выполнен код в блоке script.
Ответ 2
Когда текущий фрагмент Javascript, который выполняется, завершился и не имеет более последовательных инструкций для выполнения, тогда и только тогда механизм JS вытащит следующий элемент из очереди событий.
Итак, в вашем примере:
f();
b();
// JS is done executing here so this is where the next item will be
// pulled from the event queue to execute it
Javascript является однопоточным, что означает, что текущий поток Javascript выполняется до завершения, выполняя все инструкции в последовательности до тех пор, пока он не достигнет конца кода. Затем и только тогда он вытаскивает следующий элемент из очереди событий.
Вот некоторые другие ответы, которые могут помочь вам понять:
Как работают таймеры Javascript
Как JavaScript обрабатывает ответы AJAX в фоновом режиме? (целая связка ссылок цикла событий в этом сообщении)
Нужно ли беспокоиться о состоянии гонки с асинхронным Javascript?
Могут ли обработчики событий JS прерывать выполнение другого обработчика?
Ответ 3
Лучший способ объяснить, что стек вызовов не пуст, пока код не завершит выполнение всех соответствующих путей. SetTimeout 0 просто подталкивает ваш код к концу стека.
Когда код запускается во время выполнения, все, что будет выполняться, является частью стека вызовов, порядок будет скорректирован в зависимости от того, какие вещи вызывается, и любых вызванных вызовов времени/интервалов/асинхронных методов.
Некоторые примеры:
function foo() {
console.log('foo');
}
function bar() {
baz();
console.log('bar');
}
function baz() {
setTimeout(function() { console.log('timeout') }, 0);
console.log('baz');
}
foo();
baz();
// call stack ends here, so, timeout is logged last.
// in console
// foo
// baz
// timeout
Как вы можете видеть, панель не включена в стек выполнения, потому что она не вызывается. Если у нас есть HTML:
<div onclick="bar()">Bar runs</div>
Когда вы нажмете на этот div, вы увидите baz
, bar
, а затем timeout
, войдя в консоль, потому что тайм-аут всегда переводится в конец текущего процесса/стека вызовов.
Надеюсь, что это объяснение поможет!
Ответ 4
Простейшее возможное объяснение: когда весь синхронный код в текущем обработчике script, функции или обработчике событий завершен.
Прямо ответьте "что, если у меня есть миллионы строк..." Да - ваш вызов setTimeout
застрял в очереди и будет ждать его очереди.