Работа стека вызовов при использовании async / await - PullRequest
0 голосов
/ 21 мая 2019

Как ведет себя стек вызовов при использовании асинхронных / ожидающих функций?

const asyncFuntion=async()=>{
    //some asynchronous code
}

const first = async()=>{
    await asyncFuntion();
    console.log('first completed');
        debugger;
}

const second = ()=>{
    console.log('second completed');
        debugger;
}

function main(){
    first();
    second();
}

main();

В приведенном выше коде, когда первая точка останова встречается во втором (), я мог видеть, что стек вызовов содержитmain () и second ().А во время второй точки останова в first () стек вызовов содержал main () и first ().

Что произошло с first () во время первой точки останова.Куда это толкнул?Предполагая, что asyncFunction () занимает некоторое время.

Кто-то, пожалуйста, помогите.

1 Ответ

1 голос
/ 22 мая 2019

Прежде всего, когда вы дойдете до точки останова, которую вы нажали в second, first уже выполнено и больше не находится в стеке.

Когда мы входим в first, мы сразу же набираем await asyncFunction(). Это заставляет JavaScript вызывать asyncFunction, а затем свободно искать что-то еще, пока мы ждем его завершения. Что делает Javascript?

Ну, во-первых, мы должны разобраться asyncFunction. Мы бросаем этот вызов функции в цикл обработки событий. То есть мы помещаем это в очередь «дел, когда у нас есть время». Мы вернемся к нему, когда он будет закончен.

Теперь нам нужно найти что-нибудь еще, связанное с нашим свободным временем. JavaScript не может продолжить со следующей строки first (то есть, console.log («первый завершен»)), потому что наш await означает, что мы не можем перейти к следующей строке, пока не закончится asyncFunction.

Итак, мы ищем стек. Javascript видит, что first был вызван с основного, а first сам не был await изд. Другими словами, мы сказали Javascript, что не имеет значения, если первый асинхронный, просто продолжайте независимо. Итак, мы переходим прямо к second. Выполнив second, мы оглядываемся назад на то, что называется, и продолжаем выполнение так, как все ожидают.

Затем, в какой-то момент в будущем наш asyncFunction заканчивается. Возможно, ожидание вызова API для возврата. Возможно это ожидало в базе данных, чтобы ответить на это. Чего бы он ни ждал, сигнал отправлен, и с ним можно разобраться. Он не "вызывается" из main - внутренне функция вызывается обратно, что-то вроде обратного вызова, но принципиально вызывается с совершенно новым стеком, который будет уничтожен, как только мы закончим, вызывая оставшуюся часть функции.

Учитывая, что мы находимся в новом стеке и давно покинули «основной» кадр стека, как main и first снова окажутся в стеке, когда мы достигнем точки останова внутри него?

В течение долгого времени, если вы запускали свой код в отладчиках, простой ответ заключался в том, что они не будут. Вы просто получите функцию, в которой находитесь, и отладчик скажет вам, что она была вызвана из «асинхронного кода» или чего-то подобного.

Однако в настоящее время некоторые отладчики могут следовать ожидаемому коду обратно к обещанию, которое он разрешает (помните, await и async в основном просто синтаксический сахар поверх обещаний). Другими словами, когда ваш ожидаемый код завершается и «обещание» скрывается, ваш отладчик услужливо выясняет, как «должен» выглядеть стек. То, что он показывает, на самом деле не очень похоже на то, как механизм в конечном итоге вызвал функцию - в конце концов, она была вызвана из цикла событий. Тем не менее, я думаю, что это полезное дополнение, позволяющее всем нам держать ментальную модель нашего кода намного проще, чем то, что происходит на самом деле!

Некоторое дальнейшее чтение о том, как это работает, которое охватывает гораздо больше деталей, чем я могу здесь:

...