Отладка асинхронного JavaScript с помощью Chrome DevTools

Введение

Мощная особенность, которая делает JavaScript уникальным, — это его способность работать асинхронно с помощью функций обратного вызова. Назначение асинхронных обратных вызовов позволяет писать код, управляемый событиями, но это также делает отслеживание ошибок утомительным занятием, поскольку JavaScript не выполняется линейным образом.

К счастью, теперь в Chrome DevTools вы можете просмотреть полный стек вызовов асинхронных обратных вызовов JavaScript!

Краткий тизер-обзор стеков асинхронных вызовов.
Краткий тизер-обзор стеков асинхронных вызовов. (Скоро мы разберем ход этой демонстрации.)

После включения функции стека асинхронных вызовов в DevTools вы сможете детализировать состояние вашего веб-приложения в различные моменты времени. Пройдите полную трассировку стека для некоторых прослушивателей событий, setInterval , setTimeout , XMLHttpRequest , промисов, requestAnimationFrame , MutationObservers и других.

Проходя трассировку стека, вы также можете анализировать значение любой переменной в данный конкретный момент выполнения во время выполнения. Это как машина времени для выражений ваших часов!

Давайте включим эту функцию и рассмотрим несколько таких сценариев.

Включить асинхронную отладку в Chrome

Попробуйте эту новую функцию, включив ее в Chrome. Перейдите на панель «Источники» Chrome Canary DevTools.

Рядом с панелью «Стек вызовов» с правой стороны появился новый флажок «Асинхронный». Установите этот флажок, чтобы включить или отключить асинхронную отладку. (Хотя, как только он будет включен, вы, возможно, никогда не захотите его выключать.)

Включите или отключите функцию асинхронности.

Захват событий таймера с задержкой и ответов XHR

Вероятно, вы уже видели это раньше в Gmail:

Gmail повторяет попытку отправить электронное письмо.

Если при отправке запроса возникла проблема (либо на сервере, либо на стороне клиента возникли проблемы с сетевым подключением), Gmail автоматически попытается повторно отправить сообщение после небольшого тайм-аута.

Чтобы увидеть, как стеки асинхронных вызовов могут помочь нам анализировать события таймера с задержкой и ответы XHR, я воссоздал этот процесс с помощью макета примера Gmail . Полный код JavaScript можно найти по ссылке выше, но порядок действий следующий:

Блок-схема примера макета Gmail.
На диаграмме выше методы, выделенные синим цветом, являются наиболее полезными для этой новой функции DevTool, поскольку эти методы работают асинхронно.

Если посмотреть только на панель «Стек вызовов» в предыдущих версиях DevTools, точка останова в postOnFail() даст вам мало информации о том, откуда вызывался postOnFail() . Но посмотрите на разницу при включении асинхронных стеков:

До
Точка останова установлена ​​в макете Gmail без стеков асинхронных вызовов.
Панель «Стек вызовов» без включенной асинхронности.

Здесь вы можете видеть, что postOnFail() был инициирован обратным вызовом AJAX, но никакой дополнительной информации нет.

После
Точка останова установлена ​​в примере макета Gmail со стеками асинхронных вызовов.
Панель «Стек вызовов» с включенной асинхронностью.

Здесь вы можете видеть, что XHR был инициирован из submitHandler() . Хороший!

Если включены асинхронные стеки вызовов, вы можете просмотреть весь стек вызовов, чтобы легко увидеть, был ли запрос инициирован из submitHandler() (который происходит после нажатия кнопки отправки) или из retrySubmit() (который происходит после задержки setTimeout() ). :

submitHandler()
Точка останова установлена ​​в примере макета Gmail со стеками асинхронных вызовов
повторить отправку()
Еще одна точка останова, установленная в примере макета Gmail со стеками асинхронных вызовов.

Наблюдайте за выражениями асинхронно

Когда вы проходите полный стек вызовов, ваши наблюдаемые выражения также обновляются, чтобы отражать состояние, в котором они находились в тот момент!

Пример использования выражений наблюдения со стеками вызовов aysnc

Оцените код из прошлых областей

Помимо простого просмотра выражений, вы можете взаимодействовать со своим кодом из предыдущих областей прямо на панели консоли DevTools JavaScript.

Представьте, что вы Доктор Кто и вам нужна небольшая помощь, чтобы сравнить часы, которые были до того, как вы попали в ТАРДИС, и часы «сейчас». С консоли DevTools вы можете легко оценивать, сохранять и выполнять вычисления над значениями, полученными в разных точках выполнения.

Пример использования консоли JavaScript со стеками вызовов aysnc.
Используйте консоль JavaScript в сочетании со стеками асинхронных вызовов для отладки кода. Вышеуказанное демо можно найти здесь .

Использование DevTools для управления вашими выражениями сэкономит вам время от необходимости возвращаться к исходному коду, вносить изменения и обновлять браузер.

Разгадать связанные обещания

Если вы думали, что предыдущий макет потока Gmail было трудно разгадать без включенной функции асинхронного стека вызовов, можете ли вы представить, насколько сложнее это было бы с более сложными асинхронными потоками, такими как цепочки обещаний? Давайте вернемся к последнему примеру урока Джейка Арчибальда по обещаниям JavaScript .

Вот небольшая анимация обхода стеков вызовов в примере Джейка async-best-example.html .

До
Точка останова установлена ​​в примере обещаний без стеков асинхронных вызовов
Панель «Стек вызовов» без включенной асинхронности.

Обратите внимание, что на панели «Стек вызовов» очень мало информации при попытке отладки промисов.

После
Точка останова установлена ​​в примере обещаний со стеками асинхронных вызовов.
Панель «Стек вызовов» с включенной асинхронностью.

Ух ты! Такие обещания. Много обратных звонков.

Получите ценную информацию о вашей веб-анимации

Давайте углубимся в архивы HTML5Rocks. Помните анимацию «Leaner, Meaner, Faster» Пола Льюиса с requestAnimationFrame ?

Откройте демо-версию requestAnimationFrame и добавьте точку останова в начале метода update() (около строки 874) файла post.html. С помощью стеков асинхронных вызовов мы получаем гораздо больше информации о requestAnimationFrame , включая возможность пройти весь путь обратно к инициирующему обратному вызову события прокрутки.

До
Точка останова установлена ​​в примере requestAnimationFrame без стеков асинхронных вызовов.
Панель «Стек вызовов» без включенной асинхронности.
После
Точка останова установлена ​​в примере requestAnimationFrame со стеками асинхронных вызовов
И с включенной асинхронностью.

Отслеживайте обновления DOM при использовании MutationObserver

MutationObserver позволяет нам наблюдать изменения в DOM. В этом простом примере , когда вы нажимаете кнопку, новый узел DOM добавляется к <div class="rows"></div> .

Добавьте точку останова в nodeAdded() (строка 31) в demo.html. Если включены асинхронные стеки вызовов, вы можете вернуть стек вызовов через addNode() к начальному событию щелчка.

До
Точка останова установлена ​​в примереmutationObserver без стеков асинхронных вызовов.
Панель «Стек вызовов» без включенной асинхронности.
После
Точка останова установлена ​​в примереmutationObserver со стеками асинхронных вызовов.
И с включенной асинхронностью.

Советы по отладке JavaScript в стеках асинхронных вызовов

Назовите свои функции

Если вы склонны назначать все свои обратные вызовы как анонимные функции, вы можете вместо этого дать им имя, чтобы упростить просмотр стека вызовов.

Например, возьмем такую ​​анонимную функцию:

window.addEventListener('load', function() {
  // do something
});

И дайте ему имя, например windowLoaded() :

window.addEventListener('load', function <strong>windowLoaded</strong>(){
  // do something
});

Когда событие загрузки срабатывает, оно отображается в трассировке стека DevTools с именем функции вместо загадочного « (анонимная функция) ». Это позволяет с первого взгляда увидеть, что происходит в трассировке стека.

До
Анонимная функция.
После
Именованная функция

Изучите дальше

Напомним, что это все асинхронные обратные вызовы, в которых DevTools отображает полный стек вызовов:

  • Таймеры : вернитесь туда, где были инициализированы setTimeout() или setInterval() .
  • XHRs : вернитесь туда, где был вызван xhr.send() .
  • Кадры анимации : вернитесь туда, где был вызван requestAnimationFrame .
  • Обещания : вернитесь туда, где обещание было выполнено.
  • Object.observe : вернуться туда, где изначально был привязан обратный вызов наблюдателя.
  • MutationObservers : вернитесь туда, где было запущено событие наблюдателя мутаций.
  • window.postMessage() : обход вызовов обмена сообщениями внутри процесса.
  • DataTransferItem.getAsString()
  • API файловой системы
  • ИндекседБД
  • ВебSQL
  • Подходящие события DOM через addEventListener() : вернитесь туда, где событие было запущено. Из соображений производительности не все события DOM поддерживают функцию стеков асинхронных вызовов. Примеры доступных в настоящее время событий: «прокрутка», «хеш-изменение» и «выбор-изменение».
  • Мультимедийные события через addEventListener() : вернитесь туда, где было запущено событие. Доступные мультимедийные события включают в себя: аудио- и видеособытия (например, «воспроизведение», «пауза», «изменение скорости»), события WebRTC MediaStreamTrackList (например, «addtrack», «removetrack») и события MediaSource (например, «sourceopen»).

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

Попробуйте в Chrome. Если у вас есть отзывы об этой новой функции, напишите нам в систему отслеживания ошибок Chrome DevTools или в группу Chrome DevTools .