Что происходит с браузером при бесконечном вызове функции, вызовет ли это зависание
Что проверяют
Различаете ли вы бесконечную рекурсию и бесконечный цикл и понимаете ли, что именно при этом блокируется. Ответ «да, зависнет» без уточнений считают неполным.
Как отвечать
Начните с разделения случаев:
- бесконечная рекурсия быстро упирается в предел стека и падает с
RangeError: Maximum call stack size exceeded. Вкладка не виснет, страница продолжает работать; - бесконечный цикл
while (true)занимает главный поток навсегда: не идёт отрисовка, не обрабатываются клики и ввод, и через несколько секунд браузер предложит закрыть страницу; - бесконечная цепочка микрозадач, когда промис в
thenпланирует сам себя, вешает интерфейс так же надёжно, но стек при этом не растёт: очередь микрозадач разгребается целиком, и отрисовка до неё не доходит; - рекурсия через
setTimeoutне вешает ничего: между вызовами браузер успевает отрисоваться и обработать события. В фоновой вкладке такие таймеры ещё и замедляются примерно до одного срабатывания в секунду.
Уточните, что виснет вкладка, а не браузер: у современных браузеров отдельные процессы на сайт, соседние вкладки и сам интерфейс остаются живыми.
Что делать с тяжёлой работой: вынести в Web Worker, у него свой поток, хоть и без доступа к DOM; либо резать задачу на куски и возвращать управление циклу событий; либо считать на сервере.
Где спотыкаются
Предлагают в качестве лекарства setInterval. Он лишь размазывает ту же бесконечную работу по тикам, а если тело не успевает за интервалом, очередь колбэков растёт и становится хуже, чем было.
Второе: считают, что async и await снимают блокировку. Они управляют ожиданием, а не потоком: тяжёлый синхронный цикл внутри асинхронной функции держит главный поток ровно так же, как обычный.
Третье: надеются на оптимизацию хвостовых вызовов. В спецификации языка она есть, но из распространённых движков её реализовал только JavaScriptCore, так что в большинстве браузеров хвостовая рекурсия стек всё равно переполнит.