Function calling itself not working (infinite loop, Javascript) - javascript

I'm trying to wait and then get a message when all images in an array have completed loading (using .complete), per the answer here. As such I set up an infinite loop like the below. however, when I run this I get an error that checkForAllImagesLoaded() is not defined. This code is being run through a bookmarklet, and as such it's all wrapped up in an anonymous function construct (as below). If I re-define my function and variable outside of that construct, it works. But that seems to be a poor way to write a bookmarklet. How can I fix this so it will still recognize the function after the setTimeout?
(function() {
//var images = array of images that have started loading
function checkForAllImagesLoaded(){
for (var i = 0; i < images.length; i++) {
if (!images[i].complete) {
setTimeout('checkForAllImagesLoaded()', 20);
return;
}
}
}
checkForAllImagesLoaded();
})();

Remove the function call, and take out the quotes. If you don't put the quotes, setTimeout gets a direct reference to the function which it can invoke later. However, if inside a string such as "checkForAllImagesLoaded" or "checkForAllImagesLoaded()", then it will execute the code passed-in when the timeout occurs.
At that time, checkForAllImagesLoaded will be searched for in the global object (window) but it is not defined there, reason being why you're getting the undefined error.
Your code is wrapped in a self-calling anonymous function, and outside of it checkForAllImagesLoaded does not exist. So pass a direct reference to the function in your setTimeout call, instead of a string.
setTimeout(checkForAllImagesLoaded, 20);
setTimeout can be called with either a function (and optional arguments), or a string containing JavaScript code:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
var timeoutID = window.setTimeout(code, delay);

Remove the () in the settimeout call.
setTimeout('checkForAllImagesLoaded', 20);

With your code, you set a number of timeouts per call. You should just set the timeout once per checkForAllImagesLoaded() call and perhaps increase the waiting period (20 milliseconds is just too quick). E.g.
function checkForAllImagesLoaded() {
var allComplete=true;
var i=0;
while (i<images.length && allComplete) {
allComplete=images[i++].complete;
}
if (!allComplete) { // Any incomplete images?
setTimeout('checkForAllImagesLoaded()',1000); // Wait a second!
}
}

Related

Call Stack while using setTimeout()

I am a bit confused about setTimeout.I want to confirm whether the output for the following code will always be:
inside abc
inside sample
The code:
function abc() {
xyz();
// interactions and modifications in DOM
$("#id1").append("something");
$("#id2").val("set something");
$("#id3").after("create some dynamic element");
// 10 to 20 interaction more...
console.log('inside abc');
}
function xyz() {
setTimeout(function() {
sample();
},0);
}
function sample() {
console.log('inside sample')
}
It would be great,if somebody could explain the whole flow with the call stack.
Yes, it will always have that output.
The callback inside a setTimeout will not be called until the execution context is clear - i.e. the currently executing sequence of code has finished.
This means that even if you do
setTimeout(function () { console.log("1 second!"); }, 1000);
var start = +new Date();
while ((+new Date()) - start < 5000) {}
1 second! will not be logged any sooner than 5 seconds have passed.
setTimeout() will run asynchronously after finishing current execution block. So output should be same always:
inside abc
inside sample
Javascript internally manage an event queues internally for all async tasks. Every time it checks its async queue after finishing current execution block.
Yes, the console output will always be the same. setTimeout's callback function is executed asynchronously after the context that called it is cleared. When setTimeout is called, it places its callback function on the stack and returns to the current execution context. When that is done (in your example, when abc is fully executed), the callback function is executed, which in your example basically calls sample immediately. So your code output will never be different.
The fact that setTimeout's callbacks are themselves executed asynchronously can be seen if you placed a longer setTimeout function somewhere inside abc before calling xyz:
function abc() {
setTimeout(function(){
console.log('wait')
},1000);
xyz();
console.log('inside abc');
}
function xyz() {
setTimeout(function(){
sample();
} ,0);
}
function sample() {
console.log('inside sample');
}
abc();
...your console will log:
inside abc
inside sample
wait
To hold sample's execution until the longer timeout is complete you would need to place the setTimeout calling sample inside the longer setTimeout.
If setTimeout's callback is ever behaving differently, it's most likely due to being accidentally passed a function call instead of a function pointer, like this:
setTimeout(sample(),0);
instead of
setTimeout(sample,0)
Also, just in case you didn't know (or for others), you can add
debugger;
at any point(s) in your Javascript code and then run the code to view the callstack at that point using dev tools (in Chrome it is in the right panel under 'Sources').

why setTimeout dont work in my code? I need workable code)

I dont know why my code dont work. Please help!
$('nav').mouseout(setTimeout(function() {
$(this).removeClass('subm')
}, 1000));
Without setTimeout is work normaly.
setTimeout(...) is being called immediately. It returns the id number of the newly pending timeout. The timeout is only registered and called once here. The execution of your code is happening like this:
setTimeout(function() {
$(this).removeClass('subm')
}, 1000);
// = 2
$('nav').mouseout(2);
You need to pass .mouseout() a function that calls setTimeout each time. You also need to fix the this reference, which is different inside the setTimeout callback. This should fix both issues:
$('nav').mouseout(function() {
var self = this;
setTimeout(function() {
$(self).removeClass('subm')
}, 1000);
});
In javascript, like in most other languages, when you do this:
variable = some_function();
you're passing the return value of a function to a variable. Similarly when you do this:
a_function(another_function());
you're passing the return value of another function as an argument to a function.
This works the same in javascript, C, PHP, Ruby and even Fortran.
So, when you do this:
$('nav').mouseout(setTimeout(..));
You're passing the return value of setTimeout as an argument to mouseout. And setTimeout returns a number which can be used in clearTimeout. So you're basically doing this:
$('nav').mouseout(a_number);
What you want instead is to pass a function:
$('nav').mouseout(function(){setTimeout(..)});
Or if you find that hard to read then do this:
function handleMouseOut () {
setTimeout(...);
}
$('nav').mouseout(handleMouseOut); // note we're passing a function here
// not calling it

Javascript Delay

I understand that Javascript does not have a delay(500) method, which would delay execution for 500 milliseconds, so I have been trying to get around that by using setTimeout and setInterval.
for(var i =0; i< 10; i++){
/* Animation Code */
var doNothing = function(){var m =5;}
setTimeout(doNothing, 50);
}
However, this does not seem to work. I essentially want some code that stops the execution for n milliseconds and then continues execution.
Practically speaking, you can't do this. Deal with it and find a callback-based way instead. Typically this means putting everything that should happen after the delay in the callback itself.
For example, you can't do this to make baz wait:
foo();
setTimeout(function() {
bar();
}, 500);
baz();
so you do the only thing you can:
foo();
setTimeout(function() {
bar();
baz();
}, 500);
The setInterval() Method wait a specified number of milliseconds, and then execute a specified function, and it will continue to execute the function, once at every given time-interval.
Syntax
window.setInterval("javascript function",milliseconds);
The window.setInterval() method can be written without the window prefix.
The first parameter of setInterval() should be a function.
How to Stop the Execution?
The clearInterval() method is used to stop further executions of the function specified in the setInterval() method.
Syntax
window.clearInterval(intervalVariable)
The window.clearInterval() method can be written without the window prefix.
To be able to use the clearInterval() method, you must use a global variable when creating the interval method:
myVar=setInterval("javascript function",milliseconds);
Then you will be able to stop the execution by calling the clearInterval() method.
good refrence
If you came from the language/framework/API background, where you could suspend the execution with something like Sleep, or process user input synchronously with something like DoEvents, it won't work in JavaScript.
There is no way you can block the JavaScript event loop with something like this, for a good reason: UI responsiveness. In JavaScript, everything is asynchronous. You can use setTimeout to do something upon a timer event, but the user is still able to access the UI between the timer events or even navigate away from the page.
To address your code fragment, what you are looking for is called an asynchronous state machine. It allows to preserve the state of the code between stop/continue (in your case, it's the state of the animation, although i variable is also a part of it):
(function()
{
var i = 0;
var nextStep = function()
{
if (i<10)
{
/* Animation Code */
i++;
setTimeout(nextStep, 500);
}
}
nextStep();
})();
It will be much easier to code when all browsers support the new yield keyword:
http://pag.forbeslindesay.co.uk
On a side note, some other answers suggest using setInterval. There is a subtle but important difference between delay and interval. Delay is the time between two steps. Interval is the time since the previous step started. If each step of animation takes 200ms, and you use the interval of 500ms, the actual delay between two steps will be 300ms, not 500ms as probably expected.
setInterval() - executes a function, over and over again, at specified time intervals
To pass a function as a string, be sure to append the function name with parentheses.
window.setInterval("someFunction()", 5000);
When passing a function pointer, do not include the parentheses.
window.setInterval(someFunction, 5000);
var timer_id=setInterval(doNothing,500);
If you want to stop the execution
make the timer_id variable global
clearInterval(timer_id);
Much cleaner and readable code would be if you use RxJS
Here is an example:
Rx.Observable
.interval(1000)
.take(10)
.subscribe((x) => console.log(`${x}: ${new Date().toLocaleTimeString()}`))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>
interval - is a time delay between your animation calls. In my example
it's 1000ms
take - number of times to execute subscribe - is function
that will be called every 1000ms for 10 times (in your case it will be
your animation code)
Here some something that could help.
function delay( s , callback )
{
var fct_ref = "tmp_" + Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 6).toUpperCase();
var tmp_fct = ( callback !== undefined ) ? callback.toString().match(/^[^{]+\{(.*?)\}$/)[1] : "";
document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend","<div id='"+fct_ref+"' style='background-color:transparent;color:transparent;position:absolute;top:"+window.scrollY+"px;left:"+window.scrollX+"px;opacity:1;transition:all "+s+"s'>-</div>");
var func = new Function("return function transition"+fct_ref+"(e){ e.target.removeEventListener('transitionend' , transition"+fct_ref+", false ); "+tmp_fct+" ; document.getElementById('"+fct_ref+"').parentNode.removeChild(document.getElementById('"+fct_ref+"')); };")();
document.getElementById(""+fct_ref).addEventListener("transitionend", func , false );
document.getElementById(""+fct_ref).offsetHeight;
document.getElementById(""+fct_ref).style.opacity="0";
}
delay(1, function() { console.log("ANIMATION_1"); } );
delay(3, function() { console.log("ANIMATION_3"); } );
delay(5, function() { console.log("ANIMATION_5"); } );

setinterval causing my app to crash

Can anyone explain why this is causing my app to crash? It always crashes on the second iteration of the loop.
function FetchMetaData () {
alert("Am I being fired");
}
var timer= setInterval(FetchMetaData(),10000);
It's not "crashing"; you're just calling the function once. You should pass the function itself to setInterval(), not the result of calling the function:
var timer = setInterval(FetchMetaData, 10000);
When you write it as FetchMeData(), that means that the function should be called right then and there, and that whatever value it returns should be what's passed to setInterval(). Sometimes that makes sense, but in this case you need to pass a reference to your function. You do that in JavaScript by simply using the name of the function without calling it.

How to stop for loop execution untill DWR method execution completion which is inside the for loop

Please see this code:
for(var k=0;k<pods.length;k++){
var currentQueryList = new Array();
currentQueryList[0]=pods[k].queryname;
console.log("Hello :"+i);
queryBuilderDWRService.getDataForAllPods(currentQueryList, {callback:function(map){
podsDataMap = map;
console.log("Hello"+k+":");
var pod = pods[k];
displayPod(k, pod, false);
}});
}
In this code, the for loop is completing before the call to queryBuilderDWRService.getDataForAllPods() returns actual data, which I'm using to get data from a database.
It I try console.log("Hello"+i); it is printing Hello :1, Hello :2, Hello :3, Helllo :4, up to array length with no data from DB, meaning that the for loop is completing its execution before than queryBuilderDWRService.getDataForAllPods() returns.
What I need is that, once control is entering in the for loop, it has to complete the execution of queryBuilderDWRService.getDataForAllPods() and only then the next iteration can follow.
Thanks in advance.
You can use "asynchronous pseudo-recursion" instead of a for loop, with the general pattern that I use being:
var pods = [ ... ];
(function loop() {
if (pods.length) {
var pod = pods.shift(); // take (and remove) first element
// do something with "pod"
...
// recurse - put this inside a finish callback if "do something" is async
loop();
}
})(); // closing braces start invocation immediately
where in your case, the call to loop should be the last thing inside your callback function.
NB: This pattern can also be used to avoid the "browser not responding" error seen with longer running non-async operations by replacing the call to loop() with setTimeout(loop, 0).
This use of setTimeout turns a synchronous recursive function into an asynchronous pseudo-recursive one, in the process avoiding the possibility of any stack overflow errors.
The only downside is the 4ms minimum delay between iterations.
From the way you have it setup, the call you are making is likely async and therefore the loop will complete execution before the callback to the function you have defined. So it seems you want to build up your currentQueryList array and then make a single call to getDataForAllPods and then, in the callback, finish up the display. What is it that is returned in the map?
You should use a self executing function, called a closure.
var currentQueryList = [];
for(var i=0,l=pods.length; i<l; i++){
(function(i){
currentQueryList[0]=pods[i].queryname;
console.log('Hello :'+i);
queryBuilderDWRService.getDataForAllPods(currentQueryList, {callback:function(map){
podsDataMap = map;
console.log('Hello'+i+':');
var pod = pods[i];
displayPod(i, pod, false);
}});
})(i);
}
The problem is, when you call the event, the loop has already run through. Since i is scoped within your loop, when the event fires, it looks for the value of i, which is pods.length-1, since you have used <. You need the scope of your increment to be stored into separate memory spaces, which will be accessed when the event fires.
Note that you have made podsDataMap a global variable, and I changed your code slightly.

Categories

Resources