I have a very weird problem in my webpage. On page load, my code calls some setTimeout() functions. They work fine when the page loads the first time. By 'work find' I mean the callback functions execute after a period of time as expected. BUT if I refresh the page, the setTimeout() function appears to execute BUT the callback functions do not.
I have verified this by simply console logging like so:
....
console.log('Step 1');
setTimeout(function() {
console.log('Executed');
...
}, 10);
....
On first page load the output is:
Step 1
Executed
On refreshing that page the output is:
Step 1
Can you provide any advice why this may happen? As murphy's law has it if I create a simple HTML and JS example of the problem it does not exhibit itself. But in my web application it does. I believe I have tracked down the issue to the setTimeout being executed but the callback not executing though. I just don't know why this problem would occur or how to fix it. Getting someone to point me in the right direction to explore solutions would be extremely helpful.
Relevant information:
The webpage uses no JS frameworks (React, etc.). Its uses JQuery, JQuery UI, and a rather large JQuery plugin I've made.
The bug can be reproduced on Chrome, Firefox and Edge (Safari not tested).
Related
I'm using a framework which features auto-connecting to server on page load. I can disable it by passing options arguments, but the line that confuses me is this:
You can prevent this initial socket from connecting automatically by disabling io.sails.autoConnect before the first cycle of the event loop elapses.
My questions are:
When does the first cycle of the event loop elapses?
Is this behaviour the same across ALL modern (IE9+) browsers?
I have a bunch of scripts (in <body>) loading between the lib and my entry file. Does this affect when the first cycle elapses? EDIT: Yes, it does.
How can I ensure my code runs before the first cycle elapses?
Is this kind of implementation of auto-connect considered good practice?
The documentation for the source file is a little more explicit; it says "This can be disabled or configured by setting io.socket.options within the first cycle of the event loop."
Basically what's happening is that there exists within the library a setTimeout(fn, 0) call, which is idiomatic for starting a parallel process. However, in the JS standards it's explicitly stated that JS is single-threaded: in other words, even though setTimeout and setInterval are asynchronous they are not actually parallel in the sense that any of their code will be executing simultaneously with any other code. So they wait until the current function is over before they execute. This queueing mechanism is known as the JavaScript event loop.
I believe that what you are asked to do by the script author is to modify the source to include the relevant change, perhaps at the bottom of the file for your convenience.
It is also likely that a similar effect will be achieved by putting a <script> tag underneath the one that loads the given JS. This has not been explicitly standardized by HTML 4, but may be implicitly standardized in the new HTML 5 spec (it's a complicated interaction between different parts of the specs).
In terms of HTML5, it looks like the current specs say that there is a afterscriptexecute event and a load event which occur immediately after any remote script is loaded (or, if it's an inline script, the load event is scheduled as a task -- I am not sure when those occur). So you might be able to guarantee it without modifying the script by instead doing:
<script>
function do_not_autoload() { /* ... */ }
</script>
<script onload="do_not_autoload()" src="./path/to/sails.io.js"></script>
but I'm not sure what the compatibility table for script#onload is going to look like.
I made you a jsfiddle which can be used to grab a 'fingerprint' for different browsers to get an idea of what evaluation orders are out there in the wild. The * is the document.body.onload event. On my system it produces:
Firefox 32.0.3 : cafdbe*
Chrome 37.0.2062 : cafd*be
IE 11.0.9600 : cafd*be
In other words,
I have been asked to fix an issue with a website and I am encountering an issue with a JavaScript error.
On the home page, [removed website link], I am receiving the error Uncaught TypeError: undefined is not a function. However, the function being called on that line (line 30) should exist, as the jQuery plugin is being loaded.
The call-stack just shows a series of anonymous functions pointing towards the jQuery file. I am having trouble determining why this is producing an undefined function error.
I have tried using the Chrome debugger to step through the code where the error occurs but it just seems to highlight the jQuery source file for every step.
My question is this:
How do I go about tracking down the source of the issue when the trail is just a series of anonymous functions in the jQuery source file?
Is there something I am missing here or that I am not considering?
Thank you.
Edit:
As is it not clear, the method being called, jQuery.ContentSlider is in fact being included within the page within the file testimonials.js.
This is not just a "What's wrong with my code" question, but also an inquiry into how I handle situations such as this in the context of JavaScript & jQuery specifically.
A call stack of anonymous functions is confusing to me, and I have already attempted to take the obvious steps, such as verifying the plugin is included and that this inclusion takes place before attempting to utilize that plugin.
Sorry for the confusion.
Edit - Solution Found
It appears that although jQuery and the plugin were included prior to use, another copy of the same jQuery file was being injected by a Joomla! module. Since this was the exact same Google hosted jQuery file, it did NOT appear twice in the Resources tab in the Chrome Developer Tools. It appears that Chrome will parse jQuery twice, but doesn't show it as being included twice. So, the version with the plugin attached was being overwritten.
Thank you to those who answered. Thanks to A. Wolff for bringing that piece of information about the Resource tab to my attention.
You're loading the slider after you instantiate it.
Reverse the order of these two blocks:
<script type="text/javascript">
$(function() {
jQuery('#two').ContentSlider({
width : '440px',
height : '240px',
speed : 400,
easing : 'easeOutQuad',
textResize : true
});
});
</script>
<script src="/templates/sp/javascript/jquery.sudoSlider.min.js"></script>
Edit: To the heart of your well-formed question about debugging, generally, Undefined is not a function, especially when dealing with frameworks, is a symptom of trying to access a method before it exists, which is why your attempted function call returns undefined rather than a function.
It's almost always the result of loading a framework after trying to call it, or in an asynchronous context, of not waiting for the framework to load or do something important.
EDIT 2: The above answer is not correct, as A.Wolff points out: it's not that you must reverse the order of the two blocks, but that:
1) The second framework is probably not the one you want, or
2) You have called jQuery('#two').ContentSlider when you meant to call .sudoSlider, (or whatever is appropriate for that framework).
I am currently using PhantomJS for some doc's reports and looking in some examples i found this.
window.setTimeout(function () {
page.render(output);
console.log(output);
phantom.exit();
}, 200);
I wonder why using window.setTimeout?? It affect web rendering??
Thanks... Nick.
The page.open callback is invoked only when the page is loaded. This does not means that everything has been loaded in your page and the js has been fully executed.
In addition, the js on the web page may not be executed immediately, especially if the site uses MVC client frameworks like AngularJS or Ember.js : a lot of stuff is done after the page load event.
Using setTimeout give you a little delay to be sure that your page is fully rendered.
Sometimes 200ms is not enought and you have to increase the duration.
Another way is to use the waitfor.js example : waits until a test condition is true such as an element is visisble.
At a certain point on my javascript I have the following (using Modernizr 2.6.2):
Modernizr.load([{
load: '/path/file.js',
complete: function () {
//do stuff
}
}]);
It works great most of the time, except on IE8, about 1 time out of 3 it executes the callback first then the script being loaded. I am adding a break point on the callback and another inside file.js. Both scripts are being executed, just swapping the order at times.
I tried to isolate and reproduce the error, but I couldn't. It works fine every time when I have only this code on the page (using the exact same Modernizr file). For that reason I believe something else on my project could be interfering, but I have no clue on what to look next along the 10000 (really!) lines of js. Has anyone experienced anything similar or has any idea on what could cause this sort of behaviour?
I am not looking for a workaround for this specific case. This is one of many instances where I really would like to keep as is.
EDIT 1 - NEW INFO:
Using the network panel, I can see that two requests are being done (which is expected), but the first request always returns blank. The second request brings up the correct content. That works fine when the callback is executed after both requests are complete, but sometimes the callback is executed between the first (empty) and second request. That is when it crashes!
EDIT 2 - NEW INFO: Ok seems like I can reproduce it now. To be 100% sure on my assumptions I would have to know the javascript engine in deep, which I don't, so I could be dead wrong, but here is what I found:
Request file1.js and file2.js. If file2 finishes loading while the engine is busy executing file1's callback, file2 execution goes somewhere at the end of the queue, which means after file2's callback.
Try waiting for all of the files to load before firing the callbacks. Expand on the pattern below to suit your needs.
Modernizr.load([{
load: '/path/file.js',
complete: function () {
filesLoaded++;
checkStatus();
}
}]);
function checkStatus(){
if(filesLoaded == 2){
//do stuff
}else{
setTimeout(checkStatus, 100);
}
}
After doing some research on the subject, I think this issue may be more common than it seems at first, but has been overlooked because it is context dependent, and has different ways of being described:
Modernizr load [A, B] seems to execute B before A, how is this possible?
YepNopeJS: callback triggered before tiny JS file is fully loaded
https://github.com/SlexAxton/yepnope.js/issues/195
After reading this nice article (referenced on yepnope page), about the inconsistencies experienced on IE, I came up with a fix, which has been sent to yepnope maintainers via github:
http://github.com/SlexAxton/yepnope.js/pull/196
I don't know whether it is going to be accepted or not, but it solved my problem.
As editing a library may not always be a good idea, I would suggest the following workaround:
//instead of
complete:function(){
doStuff()
}
//try
complete:function(){
setTimeout(doStuff, 0);
}
I'd like to measure how long it takes to run the whole $().ready() scope in each of page.
For profiling specific functions I just set a new Date() variable at the beginning of the relevant part and then check how long it takes to get to the end of the relevant part.
The problem with measuring the whole $().ready scope is that it can sometimes run some of the code asynchronously and then I can not wait for it all to finish and see how long it has taken.
Is there any event which is fired once the page has completely finished running all $().ready code?
EDIT: Using Firebug or other client debuggers are not an option since I also need to collect this profiling information from website users for monitoring and graphing our web site's page load speeds
Thanks!
There will be no event fired because its virtually impossible for ready() to know when any asynchronous functions are done processing. Thus, you'll need to bake this functionality in yourself; you could use jQuery's custom events, or perhaps set a function to run on setInterval() that can introspect the environment and deduce whether or not everything else is done.
Swap out the jQuery ready function with a function that does your start and finish tracking, and calls the original method.
jQuery.ready = (function() {
var original = jQuery.ready;
return function() {
alert('starting profiler');
original();
alert('ending profiler');
};
})();
$(function() {
alert('this message will appear between the profiler messages above...');
});
Have you tried using Profiler in Firebug?