How to measures script execution and *parsing* time? - javascript

As far as I know, scripts are downloaded and executed synchronously in javascript.
Hence if we write the following code:
<script type='text/javascript'>console.time('core')</script>
<script type='text/javascript' src="guicore.js"></script>
<script type='text/javascript'>console.timeEnd('core')</script>
we'll see in console total time for download, parse and execute js.
How we can exclude parsing time? Just add similar file, but with all code commented out. More or less, this technique should work.
The problem is this just doesn't work =)
I optimized that code, reduce execution time from 90ms to 25ms, but see the same ~100±10ms time for Chrome and ~160±15ms for Firefox.
Ok, I know I could use profiler, but the question is: "how to measure js parsing time correctly" and what did I measured btw. Research.reverse-engineering is very fun, but maybe there's someone who knows that field in depth.

You cannot accurately measure script parse time independent of execution time using web APIs alone. Different browsers have different strategies for when they do parsing, and some of them will parse progressively as the script is executed, or even do "partial parsing" in cases where it's assumed a block of code is unlikely to immediately be executed (e.g. a function that is not an IIFE, see some details in the optimize-js README).
Using separate <script> tags is the most accurate way to at least capture both parsing and execution time. The only modification I would make to your snippet is this:
<script>
performance.mark('start');
</script>
<script src="myscript.js"></script>
<script>
performance.mark('end');
performance.measure('total', 'start', 'end');
</script>
Note the use of the high-precision User Timing API which, as an added bonus, will show visualizations in the Chrome/Edge/IE dev tools (and tools like Windows Performance Analyzer and WebPageTest if you're so inclined).
Technically the 3rd <script> is not necessary, as you can just append the mark/measure to the end of the 2nd script. But the 1st <script> is certainly necessary to capture all parse time. You can verify in the dev tools that the marks/measures encompass all initial parsing and execution time.

I know this is kind of an old question but I came across it while looking for a solution to this myself. You can use the dev tools in the browser of your choice to look at this but if you'd like to do it in code this is the method I ended up using.
The scriptLoadParseDuration function below will take a URL to a .js file, place it into a <script> element, and log the load/parse duration to the console.
Keep in mind that this will execute the <script> you are profiling within the current DOM context. So in the example below: jQuery is still accessible in the global scope even though the script was removed. The script could be extended to do all of this in an <iframe> to isolate it though.
function scriptLoadParseDuration(url) {
var start;
var script = document.createElement('script');
// <script> must be attached to the document to actually load the file
document.querySelector('html').appendChild(script);
// Calculate load/parse duration once script has loaded
script.addEventListener('load', function scriptLoad() {
// Calculate load/parse duration
console.log('Duration: ' + (Date.now() - start) + 'ms');
// Remove <script> from document
script.parentElement.removeChild(script);
}, false);
// Get current time in milliseconds
start = Date.now();
// Setting the `src` starts the loading. Math.random is used to make sure it is an uncached request
script.src = url + '?' + Math.floor(Math.random() * 9e9);
}
var url = 'https://code.jquery.com/jquery-3.0.0.min.js';
scriptLoadParseDuration(url);
Here is an example showing that jQuery is still in the global scope after the <script> removal.
function scriptLoadParseDuration(url) {
var start;
var script = document.createElement('script');
console.log('`jQuery` before attaching: ' + typeof jQuery);
// <script> must be attached to the document to actually load the file
document.querySelector('html').appendChild(script);
// Calculate load/parse duration once script has loaded
script.addEventListener('load', function scriptLoad() {
// Calculate load/parse duration
console.log('Duration: ' + (Date.now() - start) + 'ms');
console.log('`jQuery` once attached: ' + typeof jQuery);
// Remove <script> from document
script.parentElement.removeChild(script);
console.log('`jQuery` after detach: ' + typeof jQuery);
}, false);
// Get current time in milliseconds
start = Date.now();
// Setting the `src` starts the loading. Math.random is used to make sure it is an uncached request
script.src = url + '?' + Math.floor(Math.random() * 9e9);
}
var url = 'https://code.jquery.com/jquery-3.0.0.min.js';
scriptLoadParseDuration(url);

Open up Chrome and open the developer tools, the go to the "Timeline" tab. If you press the record button (filled in circle, bottom left) then reload the page it'll give you a fairly detailed timeline, broken down into specific types of activity (Send Request, Parse, Evaluate), timed down to the microsecond.

Very old question with a relatively new answer.
Date.now() returns a timestamp with millisecond accuracy. For an application to run at 60FPS, it must update the frame every 16ms. Our millisecond meter may not be accurate enough.
Introducing the Performace API in modern JS browsers, this allows for floating-point timestamps accurate to the microsecond.
Instead of Date.now() use window.performance.now() for measurements, there's a good guide on using the Performance API on HTML5Rocks.

Chrome DevTools actually has a hidden flag that shows V8 Parse and Compile time!
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/javascript-startup-optimization#parsecompile
Outcome looks like this:
A quick guide is also available in the doc in the blue section below:
After you enable the feature, you can profile a page and then click on "Bottom-Up" tab on the Performance tab in Chrome DevTools, and then make sure you "Group by Activity", and you should see the Compile and Parse time now.
Enjoy!

Related

page speed metrics on javascript [duplicate]

I am trying to make a webpage that, when it starts loading, uses an Interval to start a timer.
When the page fully loads, it stops the timer,
but 99% of the time i get time measurements of 0.00 or 0.01 even if it takes longer.
Occasionally, it says something that makes more sense like .28 or 3.10 at some times.
Here is the code if it helps:
var hundredthstimer = 0;
var secondplace = 0;
function addinc(){
hundredthstimer += 1;
if (inctimer == 100){
hundredthstimer = 0;
secondplace += 1;
}
}
var clockint = setInterval(addinc, 10);
function init(){
var bconv1 = document.getElementById("bconverter1");
var bconv2 = document.getElementById("bconverter2");
$(bconv2).hide();
clearInterval(clockint);
if (inctimer.len !== 2){
inctimer = "0" + inctimer;
}
alert(secondplace + "." + inctimer);
}
onload = init;
So it basically creates a variable called hundredthstimer which is increased by '1' every 10 miliseconds(.01 seconds).
Then, if this number reaches 1000(1 full second), a variable called secondsplace goes up by 1, since that is how many full seconds it has run for.
Then, it alerts secondsplace, a decimal point, and hundredthsplace as the total load time.
But the problem above with incorrect numbers still exists. Why?
Why so complicated? When you can do:
var loadTime = window.performance.timing.domContentLoadedEventEnd- window.performance.timing.navigationStart;
If you need more times check out the window.performance object:
console.log(window.performance);
This will show you the timing object:
connectEnd Time when server connection is finished.
connectStart Time just before server connection begins.
domComplete Time just before document readiness completes.
domContentLoadedEventEnd Time after DOMContentLoaded event completes.
domContentLoadedEventStart Time just before DOMContentLoaded starts.
domInteractive Time just before readiness set to interactive.
domLoading Time just before readiness set to loading.
domainLookupEnd Time after domain name lookup.
domainLookupStart Time just before domain name lookup.
fetchStart Time when the resource starts being fetched.
loadEventEnd Time when the load event is complete.
loadEventStart Time just before the load event is fired.
navigationStart Time after the previous document begins unload.
redirectCount Number of redirects since the last non-redirect.
redirectEnd Time after last redirect response ends.
redirectStart Time of fetch that initiated a redirect.
requestStart Time just before a server request.
responseEnd Time after the end of a response or connection.
responseStart Time just before the start of a response.
timing Reference to a performance timing object.
navigation Reference to performance navigation object.
performance Reference to performance object for a window.
type Type of the last non-redirect navigation event.
unloadEventEnd Time after the previous document is unloaded.
unloadEventStart Time just before the unload event is fired.
Browser Support
More Info
Don't ever use the setInterval or setTimeout functions for time measuring! They are unreliable, and it is very likely that the JS execution scheduling during a documents parsing and displaying is delayed.
Instead, use the Date object to create a timestamp when you page began loading, and calculate the difference to the time when the page has been fully loaded:
<doctype html>
<html>
<head>
<script type="text/javascript">
var timerStart = Date.now();
</script>
<!-- do all the stuff you need to do -->
</head>
<body>
<!-- put everything you need in here -->
<script type="text/javascript">
$(document).ready(function() {
console.log("Time until DOMready: ", Date.now()-timerStart);
});
$(window).load(function() {
console.log("Time until everything loaded: ", Date.now()-timerStart);
});
</script>
</body>
</html>
The answer mentioned by #HaNdTriX is a great, but we are not sure if DOM is completely loaded in the below code:
var loadTime = window.performance.timing.domContentLoadedEventEnd- window.performance.timing.navigationStart;
This works perfectly when used with onload as:
window.onload = function () {
var loadTime = window.performance.timing.domContentLoadedEventEnd-window.performance.timing.navigationStart;
console.log('Page load time is '+ loadTime);
}
Edit 1: Added some context to answer
Note: loadTime is in milliseconds, you can divide by 1000 to get seconds as mentioned by #nycynik
It is hard to make a good timing, because the performance.dominteractive is miscalulated (anyway an interesting link for timing developers).
When dom is parsed it still may load and execute deferred scripts. And inline scripts waiting for css (css blocking dom) has to be loaded also until DOMContentloaded. So it is not yet parsed?
And we have readystatechange event where we can look at readyState that unfortunately is missing "dom is parsed" that happens somewhere between "loaded" and "interactive".
Everything becomes problematic when even not the Timing API gives us a time when dom stoped parsing HTML and starting The End process. This standard say the first point has to be that "interactive" fires precisely after dom parsed! Both Chrome and FF has implemented it when document has finished loading sometime after it has parsed. They seem to (mis)interpret the standars as parsing continues beyond deferred scripts executed while people misinterpret DOMContentLoaded as something hapen before defered executing and not after. Anyway...
My recommendation for you is to read about​ Navigation Timing API. Or go the easy way and choose a oneliner of these, or run all three and look in your browsers console ...
document.addEventListener('readystatechange', function() { console.log("Fiered '" + document.readyState + "' after " + performance.now() + " ms"); });
document.addEventListener('DOMContentLoaded', function() { console.log("Fiered DOMContentLoaded after " + performance.now() + " ms"); }, false);
window.addEventListener('load', function() { console.log("Fiered load after " + performance.now() + " ms"); }, false);
The time is in milliseconds after document started. I have verified with Navigation​ Timing API.
To get seconds for exampe from the time you did var ti = performance.now() you can do parseInt(performance.now() - ti) / 1000
Instead of that kind of performance.now() subtractions the code get little shorter by User Timing API where you set marks in your code and measure between marks.
here's an expression that gives the exact value:
window.performance.timing.loadEventEnd-window.performance.timing.navigationStart

find and print webpage size in bytes and count of all <a> tags

I'm working on a CLI tool that takes URL arguments as input. I'm using jsdom package to create a pseudo-DOM inside node.js to work out with the problem. I'm using the following vanilla-js script:
console.log('Page Size (in bytes): ', (document.getElementsByTagName('HTML')[0].outerHTML.length) / 1024);
console.log('Count of <a> tags: ', document.getElementsByTagName('a').length);
<a href="">
<a href="">
<a href="">
<a href="">
<a href="">
as following inside my node.js environment:
'use strict';
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
// run loop for first argument till last
process.argv.slice(2, process.argv.length).forEach(function () {
// getting the url as argument
var pgurl = "" + process.argv[2];
console.log('For ', pgurl);
const document = (new JSDOM(``, {
url: pgurl,
pretendToBeVisual: true
})).window.document;
console.log('Page Size (in bytes): ', (document.getElementsByTagName('HTML')[0].outerHTML.length) / 1024);
console.log('Count of <a> tags: ', document.getElementsByTagName('a').length);
});
Now, since the initial in-browser solution worked for me I tried implementing on node.js and for https://facebook.com passed as an argument, it displays the following output:
For https://facebook.com
Page Size (in bytes): 0.0380859375
Count of <a> tags: 0
which remains same across all inputs, regardless of the content.
Is there any other solution to this as basically what I want is to implement browser DOM inside node.js while I pass my URLs are input argument to be processed as shown in the snippet above.
You have multiple problems going on.
The most trivial is that you are calculating a "Page Size (in bytes)" (emphasis added) but dividing the string length by 1024 which is what you'd do to count kilobytes. Note that string length is equal to byte length only in a limited number of cases.
A bigger issue is that you do not define what you mean by "page size". For instance, people often talk of "page size" as the size of the initial payload that the server sends to the browser. Using outerHTML is not reliable for that, because the payload was probably compressed. If what you want is the size of the HTML that was given to the browser to make the page, outerHTML is still not reliable because it shows you the cleaned up HTML rather that the original HTML that the browser received.
Yet another issue is that sites like Facebook construct their page using JavaScript so the JavaScript code has to run first. JSDOM by default does not execute JavaScript. The reason for this is that it is not possible for JSDOM to guarantee that JavaScript executing on a page processed by JSDOM won't be able to do malicious actions. See this explanation.
Yet another issue is that when a page is built using JavaScript there's no universal signal you can use to know that the page is "done" being built. You need to decide what event is relevant to you. e.g. I consider the page built when the "Login" button is shown. The you have to wait for that event. I'm using the term "event" loosely here. There may be an actual DOM event that corresponds to what you want or not. (Do note, however that the DOM load event does not wait for JavaScript code to do its work. It is emitted as soon as the page's HTML has been processed and before any asynchronous JavaScript that may further modify the page.)

javascript , HTML, difference between writing javascript in head and body

I am learning javascript and to tell the truth, some parts don't make sense to me. like this one. I wrote this block of code first :
<body>
<script type="text/javascript">
function people(name, age){
this.name = name;
this.age = age;
this.ret = yearsLeft;
}
function yearsLeft(){
var numYears = 65 - this.age;
return numYears;
}
var sam = new people("sam forest", 39);
var billy = new people("billy wood", 45);
document.write(billy.ret());
</script>
</body>
and I got the result. However I wrote this one after the first one and I got the same result:
<head>
<title>Javascript</title>
<script type="text/javascript">
function people(name, age){
this.name = name;
this.age = age;
this.ret = yearsLeft;
}
function yearsLeft(){
var numYears = 65 - this.age;
return numYears;
}
var sam = new people("sam forest", 39);
var billy = new people("billy wood", 45);
</script>
</head>
<body>
<script type="text/javascript">
document.write(billy.ret());
</script>
</body>
Here is my question, what is the difference , when I get the same result in both ways?
From Yahoo's Best Practices for Speeding Up Your Web Site:
The problem caused by scripts is that they block parallel downloads.
The HTTP/1.1 specification suggests that browsers download no more
than two components in parallel per hostname. If you serve your images
from multiple hostnames, you can get more than two downloads to occur
in parallel. While a script is downloading, however, the browser won't
start any other downloads, even on different hostnames.
In some situations it's not easy to move scripts to the bottom. If,
for example, the script uses document.write to insert part of the
page's content, it can't be moved lower in the page. There might also
be scoping issues. In many cases, there are ways to workaround these
situations.
An alternative suggestion that often comes up is to use deferred
scripts. The DEFER attribute indicates that the script does not
contain document.write, and is a clue to browsers that they can
continue rendering. Unfortunately, Firefox doesn't support the DEFER
attribute. In Internet Explorer, the script may be deferred, but not
as much as desired. If a script can be deferred, it can also be moved
to the bottom of the page. That will make your web pages load faster.
Therefore, in general, it is preferrable to put them at the bottom. However, it isn't always possible, and it often doesn't make that much of a difference anyway.
In many cases the result is the same, but there's a relevant difference due to the way web browser render html pages.
Since a browser reads the page content top-to-bottom, placing the javascript code within the <head> tag will cause the actual page content to be displayed after the browser has finished parsing the script. Placing it just before the </body> tag instead will let the browser display the content faster, which is usually desirable.
Another implication of the top-to-bottom rendering is related to SEO optimization: since most crawlers will inspect a fixed number of bytes at the top of a page, having those first bytes filled with javascript code will prevent the crawler from accessing the actual page content, therefore reducing the benefits of whatever you've SEO-wise.
Because you're doing pretty much the same thing. The browser is going to evaluate your javascript code sequentially, and, if it's just statements, as you putted, they're going to be executed.
So, one thing you need to pay attention is that the browser is going to evaluate your hole document (html, css, javascript), and the javascript statements that are not function definitions are going to be executed right away.

Any way to gracefully enforce a timeout limit when loading a slow external file via javascript?

I'm using javascript to include some content served up from a php file on another server. However, this other service can sometimes get flaky and either take a long time to load or will not load at all.
Is there a way in JS to try to get the external data for x number of seconds before failing and displaying a "please try again" message?
<script type="text/javascript" src="htp://otherserver.com/myscript.php"></script>
Couple issues: you can use timeout thresholds with XMLHttpRequest (aka ajax), but then since it's on an otherserver.com you cannot use XMLHttpRequest (and support all A-grade browsers) due to the Same Origin Policy restriction.
If the script introduces any kind of global name (eg any variable name, function name, etc) You can try setTimeout to keep checking for it:
var TIMELIMIT = 5; // seconds until timeout
var start = new Date;
setTimeout(function() {
// check for something introduced by your external script.
// A variable, namespace or function name here is adequate:
var scriptIncluded = 'otherServerVariable' in window;
if(!scriptIncluded) {
if ((new Date - start) / 1000 >= TIMELIMIT) {
// timed out
alert("Please try again")
}
else {
// keep waiting...
setTimeout(arguments.callee, 100)
}
}
}, 100)
The problem as I see it is you cannot cancel the request for the script. Please someone correct me if I'm wrong but removing the <script> from the DOM will still leave the browser's request for the resource active. So although you can detect that the script is taking longer than x seconds to load, you can't cancel the request.
I think you may be out of luck.
The only way I can think of doing this is to create a proxy on another (PHP-enabled) server which will fetch the data for you, but will stop when a certain timeout limit has been reached (and it can just return an empty result).
This is purely, purely theoretical:
<script> tags can be dynamically inserted into the DOM, at which point the script will be fetched and processed. This dynamic script tag injection is how some achieve cross-domain "AJAX."
I would imagine you could declare a global variable var hasLoaded = false;. At the end of the script you are attempting to load you could set that variable to true hadLoaded=true;. After injecting the script tag into the DOM you could then kickoff a setTimeout() whose callback function checks to see if "hasLoaded" is set to true. If it isn't, you can assume the script has not yet loaded fully into the browser. If it has, you can assume it has loaded completely.
Again, this is theoretical, but if you test it be sure to report back, I'm very curious.
I think that the only way to do this is take the content of the file via ajax and then set a timer. If the request finishes before the timer you can evaluate the respons with eval(that's not the better solution anyway), otherwise you can stop the ajax request and write the error message.

Making DiveIntoPython3 work in IE8 (fixing a Javascript performance issue)

I am trying to fix the performance problem with Dive Into Python 3 on IE8. Visit this page in IE8 and, after a few moments, you will see the following popup:
alt text http://dl.getdropbox.com/u/87045/permalinks/dip3-ie8-perf.png
I traced down the culprit down to this line in j/dip3.js
... find("tr:nth-child(" + (i+1) + ") td:nth-child(2)");
If I disable it (and return from the function immediately), the "Stop executing this script?" dialog does not appear as the page now loads fairly fast.
I am no Javascript/jquery expert, so I ask you fellow developers as to why this query is making IE slow. Is there a fix for it?
Edit: you can download the entire webpage (980K) for local viewing/editing.
This seems to need a bit of rewriting.
nth-child is a slow operation. You should implement the current functionality by generating classes or ids that would be common for the TDs in table and elements from the refs collection (dip3.js line 183). and then:
refs.each(function(i) {
var a = $(this);
var li = a.parents("pre").next("table").find("td."+a.attr('class'));
li.add(a).hover(function() { a.css(hip); li.css(hip); },
function() { a.css(unhip); li.css(unhip); });
});
This popup message is misleading - it doesn't actually mean that IE is running slowly, but that the number of executed script statements has exceeded a certain threshold. Even if the script executes very quickly, you'll still see this message if you go over the limit. The only way to get rid of it is to reduce the number of statements executed or edit the registry.
http://support.microsoft.com/kb/175500
I find Microsoft's implementation of this very annoying. It makes assumptions about the speed of your computer.

Categories

Resources