#function #compiler-construction #callstack
#функция #компилятор-построение #callstack
Вопрос:
Допустим, у вас есть эта последовательность функций (JavaScript)….
A(function(){
console.log('done')
})
function A(done) {
a()
B(D, done)
}
function B(x, y) {
x(function(){
c()
C(y)
d()
})
}
function C(z) {
g()
setTimeout(z, 1000)
}
function D(z) {
h()
setTimeout(z, 2000)
}
function a() {
b()
c()
}
function b() {
// ... sync stuff
}
function c() {
e()
// ... sync stuff
f()
}
function d() {
// ... sync stuff
}
Попытался сделать так, чтобы у него был своего рода сложный стек вызовов.
Мне интересно, как выглядит стек вызовов в разные моменты времени. Например, c();C(y);d()
последовательность. Когда c()
вызывается, следующей функцией, которая будет вызвана на этом уровне, является C()
. Таким образом, похоже, что он будет помещен в стек (перед оценкой c()
), который C()
является местом возврата. Затем он переходит к e()
и f()
(игнорируя это на данный момент). Затем он проверяет стек вызовов и возвращается к C()
. Затем тот же процесс. Но поскольку C()
является асинхронным, оно выполняется d()
до C()
завершения. Итак, это так:
c c c c c c c ...?
C C C C C /
e e f C d
f
Вот о чем я думаю, когда пытаюсь отобразить стек вызовов. Похоже, что это сформирует дерево. Теперь представьте, что несколько асинхронных процессов запускаются примерно в одно и то же время. Тогда это похоже на несколько ветвей дерева. Итак, вместо стека вызовов, дерево вызовов. Это заставляет меня, наконец, задаться вопросом, как именно оценивается стек вызовов. Когда следующая функция в последовательности помещается в стек вызовов и как они обновляют / удаляют последнюю завершенную функцию и находят обратный путь к следующему месту в стеке / дереве вызовов.
Интересно, не могли бы вы указать какие-либо ресурсы, которые могли бы описать это, или, возможно, даже объяснить, как будет выглядеть стек вызовов в примере, который я описал выше.
Комментарии:
1. Это действительно не вопрос сборки. Не думайте об этом как об одном стеке вызовов. Представьте себе асинхронное выполнение в виде нескольких стеков вызовов в потоках.
Ответ №1:
Когда вы вызываете функцию, вы помещаете адрес возврата в стек, а не следующую вызываемую функцию. Затем вызываемая функция создаст свой собственный фрейм в стеке (вы можете считать адрес возврата частью этого фрейма или отдельным от фрейма, в зависимости от того, как вы на это смотрите. Когда функция возвращается, она выводит свой фрейм и возвращается к адресу возврата (который также неявно или явно выводит адрес возврата — детали зависят от архитектуры процессора / виртуальной машины.
Итак, для вашего примера стек вызовов с течением времени больше похож
c c c c c C C
e f g
Комментарии:
1. Но, похоже, адрес возврата — это следующая функция, я не вижу, чем она отличается. А также не уверен, что происходит в случае асинхронности.
2. Нет, адрес возврата находится в середине текущей функции. Следующая вещь в текущей функции в этом случае — это другой вызов (следующей функции), но это может быть что угодно.