I have a small JavaScript code in which I'm trying to use setTimeout to either wait for some external input to arrive (via responseReceived variable), OR wait for a max threshold time and then quit.
Here's the code:
var MAX_WAIT_THRESHOLD = 5000;
var keepWaiting = true;
var waitInterval = 500;
var totalTimeWaited = 0;
alert("Alert1");
while(keepWaiting == true) {
setTimeout(function() {
totalTimeWaited = totalTimeWaited + waitInterval;
alert("Alert2");
if(responseReceived == true || totalTimeWaited >= MAX_WAIT_THRESHOLD) {
keepWaiting = false;
}
}, waitInterval);
}
The problem is for some reason setTimeout(..) never actually calls the anonymous function created inside it. I checked this by placing breakpoints in Chrome's JavaScript execution control, and execution never actually stops at any breakpoint placed inside the anonymous function. JavaScript's execution keeps toggling between the while .. line and setTimout(..) line. responseReceived is set elsewhere in the code. Another way of saying this is that the first alert shows (Alert1) but the second one never shows (Alert2).
What am I doing wrong ?
EDIT:
I went through the 'duplicate' question reported but I fail to see how that is relevant to my question. My question is not regarding the while loop. Rather it's about why the internal anonymous function isn't being called.
Your code killed my browser tab. :D
Why don’t you just use setInterval and get rid of the while loop?
var MAX_WAIT_THRESHOLD = 5000;
var waitInterval = 500;
var totalTimeWaited = 0;
var waiting = true;
alert("Alert1");
var interval = setInterval(function() {
totalTimeWaited += waitInterval;
alert("Alert2");
if (responseReceived || totalTimeWaited >= MAX_WAIT_THRESHOLD) {
// stop waiting
clearInterval(interval);
waiting = false;
alert("Done");
}
}, waitInterval);
This issue is related with the javascript concurrency model.
As far as I know, all your setTimeout callbacks can only be executed after the while loop ends, because that while loop is a message blocking the queue. But in this case, it never ends.There was another SO thread explaining this, but I can't find it now.
I see you're actually controlling the max time of the loop with control variables. However, since this control code is inside a setTimeout callback, it will not be pushed into the queue until the loops end, so the control var keepWaiting will never turn to false inside the loop. Should you take that control code outside the setTimeout function, it wouldn't break the browser and will at some point show the 'alert2' message -several times, in fact-:
var MAX_WAIT_THRESHOLD = 5000;
var keepWaiting = true;
var waitInterval = 500;
var totalTimeWaited = 0;
var responseReceived = false;
console.log("Alert1");
while(keepWaiting == true) {
setTimeout(function() {
console.log("Alert2");
}, waitInterval);
totalTimeWaited = totalTimeWaited + waitInterval;
if(responseReceived == true || totalTimeWaited >= MAX_WAIT_THRESHOLD) {
keepWaiting = false;
}
}
However, all the callbacks will fire at the end for the reasons exposed before.
If you want an eternal loop of setTimeouts, use recursion instead of iteration.
Related
I wanna use Chrome or Firefox Developer Tools to execute code on a website.
When I execute a "window.RR" variable lonely, it shows me the right value (it is a measure of the site's server time (in milliseconds) and every time I execute it gives me a different value). When I use it in a loop (variable c), then the variable is constant in all loops and it equals to the value of the first loop.
My code is:
var i;
b=window.RR;
for (i=0 ; i<400000 ; i++) {
c=window.RR;
if (c!==b) {
alert(c)
}
}
I expect to receive the correct value when I use it in the loop, how can I achieve this?
I assume the the window.RR variable is updated by an interval. This means that the variable is updated asynchronous.
For example, lets say that the window.RR holds the timestamp (in milliseconds) of the server, and the code below updates the window.RR every millisecond.
setInterval(function () {
window.RR += 1;
}, 1);
If you then run a loop, that may take more than 1ms to be executed
var c = window.RR;
for (var i = 0; i < 999999999999999; i++){
if (c != window.RR) { // This is always false
console.log('This will never be printed');
}
}
the window.RR will not change during the execution of the loop, because the javascript asynchronous codes does not run in parallel.
Thus, if the loop takes more than 1 ms to be executed window.RR update code will happen after the loop finish (it will have to wait any other code fired to finish before that code can be executed).
More info:
This code does not let any other code to run while the for loop is running.
var c = window.RR;
for (var i = 0; i < 999999999999999; i++){
if (c != window.RR) { // This is always false
console.log('This will never be printed');
}
}
In order to let other code to run, you will have to make each loop async.
var c = window.RR;
var loop = function(i) {
if (i < 999999999999999) {
i++;
if (c != window.RR) {
console.log('This will be printed!');
}
else {
// Allow other javascript codes to run
// So that the window.RR can be updated
setTimeout(function () {loop(i);}, 0);
}
}
};
loop(0);
I try to write a function that shakes/nudges a window popup
this.nudge = function() {
var stopped = false;
var x=10;
setTimeout(function() {
stopped = true;
},1000);
for (i=0;;i++){
if (stopped) break;
window.moveBy(0,x)
window.moveBy(x,0)
window.moveBy(0,-x)
window.moveBy(-x,0)
}
}
Problem is : it never stops !
Since JavaScript is single-threaded (as #Luaan mentioned in the comments), your infinite loop will run infinitely and prevent any timeouts or other events from being executed. The runtime won't even try to preempt your loop (break it in the middle to run other code) and will wait until your main function has returned -- which never happens.
You cannot use a busy wait or other, similar loops in JS: they prevent the browser's UI from responding and will cause any events to be deferred until execution has finished and the script thread is free.
To process your loop correctly in an event-friendly fashion, you can either set it up as a timeout every repetition or an interval, like:
this.nudge = function() {
var x = 10;
var loop = setInterval(function() {
window.moveBy(0,x)
window.moveBy(x,0)
window.moveBy(0,-x)
window.moveBy(-x,0)
}, 100);
setTimeout(function() {
clearInterval(loop);
}, 1000);
}
This simplifies your code by removing the stopped variable and simply started/clearing a deferred loop when the timeout has expired. You can adjust the timing on either function to change the rate of animation and overall duration.
What do you think about such solution?
var i = 0;
(function a(timeout, startTime) {
console.log(i++);
if (Date.now() - startTime > timeout) return;
setTimeout(function() {
/* window.moveBy(0, x);
window.moveBy(x, 0);
window.moveBy(0, -x);
window.moveBy(-x, 0); */
a(timeout, startTime);
}, 0);
})(1000, Date.now());
Maybe you need something lie this :
this.nudge = function() {
var x=10;
var ts = Date.now();
while(Date.now()-ts<1000){
window.moveBy(0,x)
window.moveBy(x,0)
window.moveBy(0,-x)
window.moveBy(-x,0)
}
}
I have a long running function. Which iterates through a large array and performs a function within each loop.
longFunction : function(){
var self = this;
var data = self.data;
for(var i=0; len = data.length; i<len; i++){
self.smallFunction(i);
}
},
smallFunction : function(index){
// Do Stuff!
}
For the most part this is fine but when I am dealing with arrays above around 1500 or so we get to the point of recieving a javascript execution alert message.
So I need to break this up. My first attempt is like so:
longFunction : function(index){
var self = this;
var data = self.data;
self.smallFunction(index);
if(data.slides[index+1){
setTimeout(function(){
self.longFunction(index+1);
},0);
}
else {
//WORK FINISHED
}
},
smallFunction : function(index){
// Do Stuff!
}
So here I am removing the loop and introducing a self calling function which increases its index each iteration. To return control to the main UI thread in order to prevent the javascript execution warning method I have added a setTimeout to allow it time to update after each iteration. The problem is that with this method getting the actual work done takes quite literally 10 times longer. What appears to be happening is although the setTimeout is set to 0, it is actually waiting more like 10ms. which on large arrays builds up very quickly. Removing the setTimeout and letting longFunction call itself gives performance comparable to the original loop method.
I need another solution, one which has comparable performance to the loop but which does not cause a javascript execution warning. Unfortunately webWorkers cannot be used in this instance.
It is important to note that I do not need a fully responsive UI during this process. Just enough to update a progress bar every few seconds.
Would breaking it up into chunks of loops be an option? I.e. perform 500 iterations at a time, stop, timeout, update progress bar, perform next 500 etc.. etc..
Is there anything better?
ANSWER:
The only solution seems to be chunking the work.
By adding the following to my self calling function I am allowing the UI to update every 250 iterations:
longFunction : function(index){
var self = this;
var data = self.data;
self.smallFunction(index);
var nextindex = i+1;
if(data.slides[nextindex){
if(nextindex % 250 === 0){
setTimeout(function(){
self.longFunction(nextindex);
},0);
}
else {
self.longFunction(nextindex);
}
}
else {
//WORK FINISHED
}
},
smallFunction : function(index){
// Do Stuff!
}
All I am doing here is checking if the next index is divisble by 250, if it is then we use a timeout to allow the main UI thread to update. If not we call it again directly. Problem solved!
Actually 1500 timeouts is nothing, so you can simply do this:
var i1 = 0
for (var i = 0; i < 1500; i++) setTimeout(function() { doSomething(i1++) }, 0)
System will queue the timeout events for you and they will be called immediately one after another. And if users click anything during execution, they will not notice any lag. And no "script is running too long" thing.
From my experiments V8 can create 500,000 timeouts per second.
UPDATE
If you need i1 passed in order to your worker function, just pass an object with it, and increment the counter inside of your function.
function doSomething(obj) {
obj.count++
...put actual code here
}
var obj = {count: 0}
for (var i = 0; i < 1500; i++) setTimeout(function() { doSomething(obj) }, 0)
Under Node.js you can aslo use setImmediate(...).
Here's some batching code modified from an earlier answer I had written:
var n = 0,
max = data.length;
batch = 100;
(function nextBatch() {
for (var i = 0; i < batch && n < max; ++i, ++n) {
myFunc(n);
}
if (n < max) {
setTimeout(nextBatch, 0);
}
})();
You might want to use requestAnimationFrame to break up your execution. Here is an example from an developers.google.com article Optimize JavaScript Execution where they even do a couple iterations a time if the task were quicker than X ms.
var taskList = breakBigTaskIntoMicroTasks(monsterTaskList);
requestAnimationFrame(processTaskList);
function processTaskList(taskStartTime) {
var taskFinishTime;
do {
// Assume the next task is pushed onto a stack.
var nextTask = taskList.pop();
// Process nextTask.
processTask(nextTask);
// Go again if there’s enough time to do the next task.
taskFinishTime = window.performance.now();
} while (taskFinishTime - taskStartTime < 3);
if (taskList.length > 0)
requestAnimationFrame(processTaskList);
}
Is there anything better?
If you’re OK with it working only in modern browsers – then you should look into “Web Workers”, that let you execute JS in the background.
https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers
I am doing some long polling (ajax) and I am looping the following portion of code.. There is code being executed above and below. This code is part of an internal messaging system. A certain portion of the page wil blink when a message arrives. If the user checks the message, it will remove the dash_notify from the JSON response, which needs to turn off the blinking. See below:
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
timer = setInterval(blink, 10);
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
}
console.log("initiate_dash");
blinking($(x));
} else if (!data.dash_notify) {
console.log("good");
clearInterval(timer);
}
The following JSON response that gets sent to this code is:
{"current_date_time":"January 8, 2013 - 4:02 pm","dash_notify":"1"}
It understand the initial blink IF the above data gets passed. If the following gets passed:
{"current_date_time":"January 8, 2013 - 4:02 pm"}
Then it throws an error:
Uncaught ReferenceError: timer is not defined
I cannot figure out how to fix the "else" portion working properly. If the code is initiated when the full dash_notify:1 response is sent, it works perfect. The button will blink, then if the user checks the message, it will no longer send dash_notify:1 and the button stops blinking. But if the code is initiated when dash_notify:1 is NOT set, it doesn't know what to do with the ClearInterval.
Basically I need the else portion fixed.
I have tried using different typeOf === undefined snippets, but it doesn't work.
Any help is appreciated.
Thank you!
EDIT:
This is currently working.. Timer is now defined above the statement
if(data.dash_notify == '1'){
var x = '#dash_notif_blink';
console.log("initiate_dash");
blinking($(x));
}else if (typeof timer != "undefined" && timer) {
clearInterval(timer);
}
}
This is working, but sometimes it trys to kill the timer but it doesn't actually do it. This happens every so often.
Looks like it's not working because timer doesn't exist outside your inner blinking function. I'm making an assumption here that you don't have var timer; somewhere outside the blinking function, which is strongly likely given the error you're getting.
Why this is happening:
If I'm right, and you're not declaring timer anywhere else in your code, then var timer is being implicitly added to the beginning of the blinking function:
function blinking(x) {
var timer;
timer = setInterval(blink, 10);
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
}
That makes timer a local variable inside blinking. Since you never pass it out of the closure, it doesn't exist once you're outside that function. So either you need to pull timer into the outer context (option 1) or make it available from inside blinking (option 2).
What to do:
If you want access to timer outside of that closure, you'll have to do one of two things:
1: Declare timer outside of blinking:
var timer = null;
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
//etc...
2: Make it the return value of blinking:
var t;
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
var timer = setInterval(blink, 10); //note the var keyword for best practice
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
return timer;
}
console.log("initiate_dash");
t = blinking($(x));
} else if (!data.dash_notify) {
console.log("good");
clearInterval(t);
}
Either one of these will work, and is more or less the same in terms of polluting the outer namespace. I prefer Option 2, because I feel like it's easier to work with a local variable until you need to return it.
Edit:
Your comment said the loop runs infinitely, which means you're creating a brand new interval and reassigning the timer variable every time. This is a separate problem from the one I described above. The old interval is still out there, timer just doesn't point to it anymore. So how can clearInterval(timer) clear out all those intervals? It can't, it can only clear the most recent one.
Basically, you've got a whole bunch of timers all trying to make the thing blink at once.
How you deal with this depends on what you're trying to do. The simplest thing would be to keep no more than one interval running at once, which means you have to clear timer every time.
//same as option 1 above except for `clearInterval(timer)` at the
//beginning of `blinking`
var timer = null;
if (data.dash_notify == '1') {
var x = '#dash_notif_blink';
function blinking(x) {
clearInterval(timer);
timer = setInterval(blink, 10);
If you need multiple timers running, you'll have to keep track of them all in an array or something:
var timers = [];
//...
function blinking(x) {
timers.push(setInterval(blink, 10));
//...
} else if (!data.dash_notify) {
timers.forEach(function(timer) {
clearInterval(timer);
});
}
Not sure what you did wrong with your typeof check since you did not actually show the whole code, but it should look something like this:
if (typeof timer != "undefined" && timer) {
clearInterval(timer);
}
Basically define your variable timer before you enter your checking procedure (loop?):
var timer;
... some code ...
if ( data.dash_notify && data.dash_notify == '1') {
...
} else if (!data.dash_notify) {
clearInterval(timer);
}
You can call clearInterval( whatever ) without any consequences. Even if whatever is null, undefined, string and so on. Just make sure timer exist.
Passing an invalid ID to clearTimeout does not have any effect (and
doesn't throw an exception). (MDN)
You're getting that error because timer is only declared/initialized in the blinking function. In the place where you call clearInterval(timer), timer doesn't exist.
This is now working beautifully.
*Thank you to everyone who helped!*
if(data.dash_notify === '1' && t === null ){
var x = '#dash_notif_blink';
function blinking(x) {
var timer = setInterval(blink, 10); //note the var keyword for best practice
function blink() {
x.fadeOut(400, function () {
x.fadeIn(400);
});
}
return timer;
}
console.log('initiate_dash_alert');
// Data is passed. Parse to see if dash alert should be called. Secondary protection for
// multiple timer creation.
if(t){return;}else{t = blinking($(x));}
}else if (!data.dash_notify){
clearInterval(t);
console.log('clear_dash_alert');
t = null;
}else{
console.log(t);
console.log('no_push_events');
}
I have a javascript function that is being built to animate the collapse of a div, and then proceed with other jobs. The code is as follows:
function newsFeed() {
var self = this;
this.collapse = function(listingID,orig_height,curr_height,opacity) {
var listing = document.getElementById(listingID);
var reduceBy = 5;
if(curr_height > reduceBy) {
curr_height = curr_height-reduceBy;
listing.style.overflow = "hidden";
listing.style.height = (curr_height-40) + "px";
if(opacity > 0) {
opacity = opacity - 10;
var opaque = (opacity / 100);
listing.style.opacity=opaque;
listing.style.MozOpacity=opaque;
listing.style.filter='alpha(opacity='+opacity+')';
}
setTimeout(function() { self.collapse(listingID,orig_height,curr_height,opacity); },1);
}else{
return true;
}
}
this.remove = function(listingID) {
var listing = document.getElementById(listingID);
var currHeight = listing.offsetHeight;
if (this.collapse(listingID,currHeight,currHeight,100)) {
// DO SOME OTHER STUFF
}
}
}
var newsFeed = new newsFeed();
newsFeed.remove('closeMe');
I cannot get the this.remove function to wait while this.collapse finishes and returns true. Is this impossible? What is the best way to go on?
Important: I would like to be able to use this.collapse with other functions yet to be built in the same fashion as I do here.
I cannot get the this.remove function to wait while this.collapse finishes
That is correct, it is impossible to do so. In JavaScript there is a single flow of execution. When the browser calls your code you can do some processing, but for anything further to occur (timeouts or event calls) you must return control to the browser.
‘Asynchronous’ processes like collapse() are done by setting timeouts, so control must be returned to the browser many times; when remove() calls collapse() the first time it returns immediately after the first timeout is set; that timeout cannot be fired until remove() itself returns, so your 'if' code will only ever execute if the very first call to collapse() was the last frame of animation (ie. the element was 5px or smaller already). Otherwise collapse()'s ‘return true’ will just be returning true to the browser's timeout-caller, which doesn't care at all what value you return to it.
Some languages give you tools such as threads or coroutines that can allow an asynchronous routine to be run from a synchronous routine; JavaScript does not. Instead, remove() must supply collapse() with a callback function it can call itself on the last frame.
There is no way you can pause the execution in Javascript till something else happens. All you can do is attach a callback function to collapse to call after it is done executing the final step.
As a sidenote, jQuery provides functions like fade(), animate() etc and supports queuing. If you don't want to use jQuery, you can still look at the code to see how it's implemented.
See the examples in this page.
setTimeout is not a "sleep". The function will end right there and return "undefined".
To manage that, I think you should do something like:
var newsFeed = new newsFeed();
newsFeed.onaftercollapse = function () {
newsFeed.remove('closeMe'); // "newsFeed" or "self"? must test
};
And then instead of return true;, the collapse() will end with:
if (self.onaftercollapse) self.onaftercollapse();
This example demonstrates how to check if a function is complete.
function foo() {
foo.complete = false;
// your code here
foo.complete = true;
}
foo.complete = false;
if (foo.complete) { // foo execution complete
// your code here
}
This code demonstrates how to check if a function has been run once.
function foo() {
// your code here
foo.ranOnce || (foo.ranOnce = true);
}
foo.ranOnce = false;
if (foo.ranOnce) { // foo execution complete at least once
// your code here
}