📜 ⬆️ ⬇️

Debug asynchronous javascript using Chrome DevTools

Introduction


The ability to work asynchronously using callback functions (hereinafter simply returning functions) is a distinctive feature of JavaScript. Using asynchronous return functions allows you to write event-oriented code, but also adds a bunch of problems, because the code stops running in a linear sequence.

Fortunately, now in Chrome Canary DevTools you can track the entire call stack of asynchronous functions in JavaScript!


A small presentation of the stack of asynchronous calls.

As soon as you enable support for the asynchronous function call stack in DevTools, you can get detailed information about the status of your web application at any time. Stroll through the full stack of your event handlers, setInterval, setTimeout, XMLHttpRequest, promises, requestAnimationFrame, MutationObservers, and so on.
')
In addition to the fact that you can view the call stack, you can also analyze the values ​​of any variables at any time during the execution of functions. This is like a time machine for observing expressions.

Let's turn on support for this functionality and run through several scenarios.

Enable asynchronous debugging support in Chrome Canary


Try the new functionality, including its support in Chrome Canary (build 35 and higher). Open the Sources page in Chrome Canary DevTools.

The “Async” checkbox is located on the panel next to the Call Stack panel on the right side. Depending on the position of this checkbox, the ability to debug asynchronous functions will be turned on or off (although once you turn it on, you will most likely never want to turn it off).


Interception of deferred events and XHR responses


You may have seen this before in Gmail:



If there is an error in sending the request (there are problems on the server side or problems with the connection on the client side), Gmail will try to send a letter to a short time.

To demonstrate how the asynchronous call stack can help us analyze deferred events and XHR responses, I described the situation described above with an example of a Gmail stub . All the JavaScript code of the example can be found at the link above. The algorithm of his work is as follows:


In the diagram above, the methods highlighted in blue are the main places to use the new features of DevTools, since these methods are executed asynchronously.

If you only look at the Call Stack panel in previous versions of DevTools, a breakpoint in the postOnFail () function will give you only a little information about where the postOnFail () function was called. And now let's take a look at the difference with the case when we work with the ability to debug asynchronous calls:
BeforeAfter
Call Stack Panel Without Asynchronous Request Support
Here you can see that the postOnFail () function call originated from the returned AJAX function, but nothing more.
Call Stack Panel with asynchronous request support
Here you can see that the XHR was called from submitHandler (), which was called by the click event handler declared in scripts.js. Cool!

When using the asynchronous call stack, we can easily view all call chains. For example, we can determine if the call to the submitHandler () method was initiated in the same way as described above, or via retrySubmit () as described below:



Using the call stack panel, you can also tell whether the triggering of a user interface event (for example, a click), a setTimeout () delay, or an asynchronous return function has led to a breakpoint.

Watch expressions asynchronously


When you view your call stack, your expressions added to the tracking will be updated to reflect the state they took at the specified time!



Execute code from previous states


In addition to simply observing expressions, you can also interact with your code from previous states directly in the DevTools JavaScript console.

Imagine that you are a Doctor Who (Dr.. Who) and you need a little help comparing the hours that you had until this point in the Tardis and now. From the DevTools console, you can easily execute, save, and perform calculations on values ​​from different execution points.


Use the JavaScript console to debug the asynchronous call stack of your code.
An example above can be found here.

Staying in DevTools to manipulate your expressions, you save time that would take you to return to previous code states, make changes, and refresh the browser page.

On the way: Debugging promise chains


If in the last Gmail example you had a hard time unraveling the stack of asynchronous calls without the new DevTools feature, then you can probably imagine how much more difficult it will be with more complex asynchronous queues, for example, with a chain of promises? Let's go over the last example from the Jess Archibald promise in JavaScript training .


Javascript promises flowchart

Here is a small animation of working with the call stack in the best asynchronous example from Jake:

Before:

Call Stack Panel without asynchronous debugging
Notice how little information the call stack panel provides when we debug promises.

After:

Call Stack Panel with Asynchronous Debug Support
Wow, what promises! A huge number of returned functions.

Note:

The promise support for the call stack will be ready very soon, as soon as the realization of the promises grows from the version in Blink to the final version in V8.

In the spirit of going back in time, if you want to look at asynchronous call stacks for promises today, you can use Chrome 33 or Chrome 34 . Go to chrome: // flags / # enable-devtools-experiments and enable experimental DevTools support. After rebooting Canary, go to the DevTools settings, there you will be able to enable support for asynchronous call stacks .


Dive deeper into web animation


Let's go deeper into the HTML5Rocks archives. Remember the article by Paula Lewis “More compact, productive and faster animations using requestAnimationFrame”?

Open the requestAnimationFrame demo and add a breakpoint at the beginning of the update () method (that's about 874 lines) of the post.html file. With an asynchronous call stack, we can “plunge deeper” into requestAnimationFrame. And, just like in the example with the Gmail stub, we have the opportunity to walk back to the starting event (this is a scroll event).
BeforeAfter

Track DOM changes with MutationObserver


MutationObserver allows us to monitor events in the DOM. Consider a simple example: when you click a button, a new DOM element is added to the block.

Add a new breakpoint in the nodeAdded () function (line 31) in the demo.html file. Using the asynchronous call stack, you can go back through the call history through the addNode () method to the initial event of the click.
BeforeAfter

Notes on debugging javascript code using the asynchronous call stack


Call your functions

If you are used to adding all your returned functions as anonymous functions, then you may now need to reconsider your decision and start giving names to functions that will help you debug using the asynchronous call stack even easier.

For example, take an anonymous function:
window.addEventListener('load', function(){ // do something }); 

And give her a name. Let's say windowLoaded ():

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

When a document load event occurs, this will be reflected in the stack output using DevTools, where instead of the mystical name "(anonymous function)", the full name of the function will appear. This will significantly simplify the readability of the call history, and in most cases will allow us to understand at a glance what is happening there.
BeforeAfter

What's next


Summarizing all of the above, all these asynchronous return functions will display the full call stack:


You can also view the full stack of changes in the near future for the following JavaScript API:


The ability to view the entire stack of your return functions should be raising your hair. This feature in DevTools will be especially useful when several asynchronous events happen depending on each other, or when an uncaught error message occurs in one of your asynchronous return functions.

Give them a chance at Chrome Canary. If you have something to tell us about this, email us in the Chrome DevTools group or send us bugs in our Chrome DevTools bug tracker.

Source: https://habr.com/ru/post/218397/


All Articles