variable timer with setTimeout() inside jQuery .each - javascript

I want to cycle through a bunch of jpg pictures as a slideshow. I have been using setInterval with success so far. However, I now want to have each slide show for a custom time. For example slide 1 shows for 6 seconds, then slide 2 for 3 seconds, etc. I tried the following code:
var sl = [["PodLoop1.jpg", 6000], ["PodLoop2.jpg", 3000]];
$.each(sl, function(i, value) {
fl = '<img src="media/' + value[0] + '" height="100%">'
setTimeout(function(){
$("#InnerMedia").html(fl);
if (i >= sl.length) {
window.location.href = "./media.php"; // refresh to restart from the beginning
}
}, value[1])
});
But this doesn't work. It just jumps to the last slide in the list. What am I doing wrong? I have looked at several similar questions on SO, for example this one, but none of them seem to deal with a variable timer.

i will never be equal sl.length
change the code to i >= sl.length-1

Your issue is you are using .each loop.
setTimeout
What setTimeout does is it registers an event at designated time. Now when you run this in loop, this will register 2 events, 1 at after 3secs and another at after 6secs. So it runs correctly.
To fix this, you will have to chain initialisation of these setTimeouts. You will have to register new event inside another one.
Sample
function createTimeout(arr, index){
if(!arr[index] == undefined) return;
setTimeout(function(){
console.log(arr[index][0]);
createTimeout(arr, ++index)
}, arr[index][1])
}
var sl = [["PodLoop1.jpg", 6000], ["PodLoop2.jpg", 3000]];
createTimeout(sl, 0);

Related

How to stop repeating of variables of "setInterval" Javascript

I'm a beginner at JS , Now am programming a javascript app
in the app there is a button when the user clicks on it, a door supposed to be opened for 10 seconds then close again
I used "set interval" to do this function by :
var rVars = []
rVars.push( {name:"door", val:1} ) // 1 for open , 0 for close
_server.setRoomVariables(r, user, rVars)
var params = {}
params.user = user
params.r = r
myInterval = setInterval("TimeForClose", 20000, params)
This is the code responsible for closing the door after 10 seconds
function TimeForClose(params){
var rVars = []
rVars.push( {name:"door", val:0} )
_server.setRoomVariables(params.r, params.user, rVars)}
And it was done successfully, but the problem was that I found the function "TimeForclose" repeats every 10 seconds , which makes when another user click on the button and door opens for him, the first one repeated function which still repeats will close the door for the second user too and both functions of each of them will be repeated , and so on .
So I read I have to use "clear interval" or timer to stop repeating but when I used it, it didn't stop the repeating so I think I used it Incorrectly
So what's the best way to stop repeating? ( I just want the function to occurs once when someone click on a button then it stops ) .
I hope you give me the correct syntax of the code
Yes, as u said u can use clearInterval() after closing of that door window, try that once
If you need to only execute once, you can use setTimeout
change setIntervalm to setTimeout
myInterval = setTimeout("TimeForClose", 20000, params)
The function is only executed once. If you need to repeat execution, use the setInterval() method.
If you need to repeat excution and stop based on some external event you can use clearTimeout()

Sometimes, jQuery only updates elements on first page load

I'm learning javascript by creating a program which requests an API and dispays various properties (price in this example) to html. I have a few questions about my code and some problems I've been facing.
1). I have a bunch of $.getJSON functions corresponding to each value that I want to retrieve. I put them all in a a single 2 min. timer. When the page FIRST loads, however, some of the html elements fail to load at all. But if I refresh the page, they sometimes do load. If I refresh again, they might not load at all again. Every time I refresh, there's like a 10% chance of that particular function not inserting the content in the element. If it does load and I leave the page open, it will correctly function (update its value and html element every 2 mins and add/remove the green and red classes). If it doesn't load and I leave the page open, it will correctly function in 2 mins when the 2nd api request is made. I have already tested that the variables have some value (are not null) before and after each $('#price').text('$' + price);.
Here's an example of a function that does that:
var tempPrice;
var myVar = setInterval(myTimer, 1200000);
myTimer();
function myTimer() {
$.getJSON(link, function (json) {
$.each(json, function (index, value) {
if (value.id == "price") {
var price = value.price_eur;
if (!tempPrice) {
$('#price').text('$' + price);
tempPrice = parseFloat(price);
}
if (parseFloat(price) !== tempPrice) {
$('#price').text('$' + price).removeClass();
if (parseFloat(price) > tempPrice) {
setTimeout(function () {
$('#price').addClass("green");
}, 1);
} else {
setTimeout(function () {
$('#price').addClass("red");
}, 1);
}
tempPrice = parseFloat(price);
}
}
});
});
// Many more $.getJSON functions below...
}
If I run this function alone on either jsfiddle or my dev server (flask), it works fine. It only breaks down when I use it in conjunction with more api requests. If I remember correctly, I didn't have this problem before when I used to have a separate timer for each $.getJSON function and put each in its own <script> tag directly in html.
2) I know I can loop through the json instead of using $.each. How else can I improve the code?
1
As for the problem you're having with the inconsistent behavior of the initial page loading, it's because you are executing JavaScript before giving the browser the time to load the page fully first. You can solve this simply by waiting for the page the load, and then executing your code.
Example in jQuery:
$(document).ready(function() {
// Page is loaded, execute code...
});
2
To help you improve the way you're handling the supplied JSON data, a sample of the data would be useful.

JavaScript callback - multithreading

I want to develope a very simple strategic game.
You can find an example on http://wiesenberg.info/hope/ !
If you click on a button, that project will be created (progressbar). After it is finished, the project will show up on the playground. As you can see it works if you click one by one. But once you click a button twice (start 2 project of the same kind), the second project will not be finished because the first has not finished and increased the variable. Is there an easy way of doing multithreading or just solving my bug?
I also want to add a function that you can stop the project by clicking on the progressbar. I tried it out, it dissapears in the list, but the icon still shows up, because the function didnt get that another function was executed. so I also need a break function for the progressbar. I hope you undertood my problem! Thanks
Heres the important code:
function addSygehus() {
sygehusBarID++;
sygehusID++;
$("#addProject").append("<div id=sygehus" + sygehusID +
" class='progress progress-striped active'><div class='progress-bar progress-bar-success' id=sygehusbar"
+ sygehusBarID + " aria-valuetransitiongoal='100'>Sygehus</div></div>");
function countdown(callback) {
var bar = document.getElementById("sygehusbar"+sygehusBarID),
time = 0, max = firsttime,
int = setInterval(function() {
bar.style.width = Math.floor(100 * time++ / max) + '%';
if (time - 1 == max) {
clearInterval(int);
// 600ms - width animation time
callback && setTimeout(callback, 600);
}
}, 1000);
}
countdown(function() {
$("#sygehus" + sygehusID).remove();
$("#sygehusPic" + sygehusID).show(1000);
});
}
I would look at JQuery Deferred (http://api.jquery.com/jQuery.Deferred/) and Promises Documentation in general to manage what your trying to achieve. So you can apply some control to the order in which the asynchronous calls occur.
You've also got Q.js (https://github.com/kriskowal/q) is a nice library for managing these kinds of scenarios.
Otherwise you have web workers as already mentioned for emulating multithreaded behaviour but I don't think that's what your looking for.

setTimeout is causing infinite while loop

This is my first time using a while loop in a practical application so please forgive my ignorance.
I am creating a webpage that demonstrates the costs of using a lightbulb over time.
At this stage I am trying to use a while loop to update and display the number of hours that have passed since the user clicked the light switch. (1 hour represents 1 second of realtime)
When I set breakpoints on firebug, everything operates normally until I get to the setTimeout method within my while loop. After it breaks at the setTimeout method and I click continue, it immediately breaks at the same place again, without actually executing anything else.
When I don't set breakpoints, it freezes firefox and I have to stop script execution.
I rechecked to make sure that I am using setTimeout properly. Now I'm not even sure where to check or what to search for because I don't understand whats going wrong. Even just a hint of what I might check or research would be immensely helpful.
I have tried to comment the code as verbosely as possible. I'll be happy to clarify something if needed.
I would highly recommend taking a look at the jsfiddle:
JS FIDDLE
But here is my code:
My JS
$(document).ready(function () {
//set image to default off position
$('#lightswitch').css("background-image", "url(http://www.austinlowery.com/graphics/offswitch.png)");
// setup the lifetime hours of the lightbulb for later use
var lifetimeHours = 0;
// setup function to update the calculated lifetime hours number on the webpage to
// be called later
function updateLifetimeHoursHtml (lifetimeHours) {
$('#lifetimeHours').html(lifetimeHours);
}
// set up function to to send to setTimeout
function updateNumbers () {
// increment lifetimeHours by one
lifetimeHours = lifetimeHours++;
// call function to update the webpage with the new number result
updateLifetimeHoursHtml(lifetimeHours);
}
// When the lightswitch on the webpage is clicked, the user should see the
// lifetime hours update every second until the user clicks the switch again
// which will then display the off graphic and pause the updating of the lifetime
// hours
$('#lightswitch').click(function(){
// if the lightswitch is off:
if ($('#lightswitch').attr('state') == 'off') {
// set switch to on
$('#lightswitch').attr('state', 'on');
// update graphic to reflect state change
$('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/onswitch.png)");
// start updating the lifetime hours number on the webpage
// while the #lightswitch div is in the on state:
while ($('#lightswitch').attr('state') == 'on'){
//call update numbers every second
setTimeout('updateNumbers()', 1000);
}
// the lightswich was not in the off state so it must be on
}else{
// change the state of the switch to off
$('#lightswitch').attr('state', 'off');
// update graphic to reflect state change
$('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/offswitch.png)");
};
});
});
My HTML
<div id="container">
<div id="lightswitch" state="off"> </div>
<span>After </span><span id="lifetimehours">0</span><span> lifetime hours:</span>
<br><br>
<span><b>You have spent:</b></span>
<br><br>
<span id="dollaramoutelectricity"></span><span> on electricty</span>
<br>
<span id="mainttime"></span><span> on maintenace</span>
<br>
<span id="dollaramountbulbs"></span><span> on replacement bulbs</span>
<br><br>
<span><b>You have:</b></span>
<br><br>
<span>Produced </span><span id="amountgreenhousegasses"></span><span> of greenhouse gasses</span>
<br>
<span>Sent </span><span id="amounttrash"></span><span> of trash to the dump</span>
<br>
<span>Used </span><span id="amountelectricty"></span><span> of electricity</span>
</div>
var switchTimer;
$('#lightswitch').click(function(){
if ($('#lightswitch').attr('state') == 'off') {
$('#lightswitch').attr('state', 'on');
switchTimer = setInterval(updateNumbers, 1000);
} else {
$('#lightswitch').attr('state', 'off');
$('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/offswitch.png)");
clearInterval(switchTimer);
};
Javascript is an event based language. This means that codes doesn't run constantly. It only runs when there is an event. By using a while loop you have basically frozen it - the javascript is constantly running inside that loop. That's fine for languages like C, which have to have something running all the time.
But it's a mistake for javascript. For javascript you have to code it to respond to an event, then stop. The setInterval constantly generates an event for you, running a function every xx milliseconds.
In between runs of the timer no code is running! This is important to remember.
JavaScript uses an event loop. Because your while loop never yields (#lightswitch's state will forever be on because the infinite loop locks the UI, preventing the user from turing it off), the event loop never runs again, and the callback you register with setTimeout never gets a chance to execute.
What you're really looking to do is update your counter with a setInterval function.
setInterval(updateNumbers, 1000);
Based on yngum's answer,
I think it's better like this:
if ($('#lightswitch').attr('state') == 'off') {
$('#lightswitch').attr('state', 'on');
$('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/onswitch.png)");
updateNumbers();
}
And then
function updateNumbers () {
lifetimeHours++;
updateLifetimeHoursHtml(lifetimeHours);
if($('#lightswitch').attr('state') == 'on'){
setTimeout(updateNumbers,1000);
}
}
See it here: http://jsfiddle.net/tdS3A/24/
But if you want maximum precision, you should store new Date().getTime(), because doing setTimeout or setInterval of 1 second doesn't ensure you that it will be called each second...
function updateNumbers () {
// call function to update the webpage with the new number result
updateLifetimeHoursHtml(lifetimeHours+(new Date().getTime()-timerStart)/1000);
// call updateNumbers() again
if($('#lightswitch').attr('state') == 'on'){
timer=setTimeout(updateNumbers,1000);
}
}
$('#lightswitch').click(function(){
// if the lightswitch is off:
if ($('#lightswitch').attr('state') == 'off') {
// set switch to on
$('#lightswitch').attr('state', 'on');
// update graphic to reflect state change
$('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/onswitch.png)");
// update the lifetime hours number on the webpage
timerStart=new Date().getTime();
timer=setTimeout(updateNumbers,1000);
// the lightswich was not in the off state so it must be on
}else{
// change the state of the switch to off
lifetimeHours+=(new Date().getTime()-timerStart)/1000;
clearTimeout(timer);
$('#lightswitch').attr('state', 'off');
// update graphic to reflect state change
$('#lightswitch').css("background-image", "url(http://austinlowery.com/graphics/offswitch.png)");
};
});
See it here: http://jsfiddle.net/tdS3A/26/
Even if you want the number with maximum precision because you want the calculations to be precise, maybe you want to round that value when you show it to the user. Then, use
function updateLifetimeHoursHtml (lifetimeHours) {
$('#lifetimehours').html(Math.round(lifetimeHours));
}
Since setTimeout is generally preferred over setInterval, another solution is
Change
while ($('#lightswitch').attr('state') == 'on'){
//call update numbers every second
setTimeout('updateNumbers()', 1000);
To
setTimeout(function() {
updateNumbers();
if ($('#lightswitch').attr('state') == 'on')
setTimeout(arguments.callee, 1000);
}, 0);
This will check ur lightswitch every second, and stop when it is off.

In JavaScript, How to make make changes to document elements while complex functions are performed?

I'm processing a large amount of data with JavaScript. I put a circular gif and a progress div to show how much progress has been done in creating the model. However, when it gets to the bulky part of code processing, the loading gif stops spinning and the percentage updating stops working. (until the very end for a split second)
This is the block of code that freezes the gif.
// convert binary normals to ascii
for(i=0;i<norms.length;i++){ //<-- the array length is about 200,000, and could be larger
normals.push(toAscii(norms[i], mins[5], maxes[5])); //nx
normals.push(toAscii(norms[i+1], mins[6], maxes[6]));//ny
normals.push(toAscii(norms[i+2], mins[7], maxes[7]));//nz
i = i+2; //skip next 2 as they're already converted
percentComplete = (normals.length/norms.length)*100;
percentComplete = Math.round(percentComplete);
document.getElementById('loadingScrn').innerHTML = "Processing "
+percentComplete + "%" + " Complete"; //<-- The loading gif is right below this element on the webpage and neither update while this function is running
}
How can I get the browser update the display while JavaScript functions process large data? Is there a way to thread activities so that both updating the Document and JavaScript processing occur simultaneously?
JavaScript runs on the same thread as the browser GUI in most cases (or the tab's GUI, if each tab is given its own process). You will have to break the work into small pieces and schedule the next piece from the currently-executing one using setTimeout().
For example, this might work in your case:
var i = 0;
function doWork() {
do {
// One iteration here...
i++;
} while (i % 100 != 0 && i < norms.length);
// ^^^
// Break work into pieces of 100 elements each; adjust this
// number as needed.
if (i < norms.length) {
setTimeout(doWork, 1);
}
}
setTimeout(doWork, 1);
See this example jsfiddle.
You are correctly observing that JavaScript code runs in the same thread as the document's interface, blocking it when you perform large operations.
Web Workers are a JavaScript feature that is designed to help solve this problem. It allows you to spawn new threads that run along side the document, and communicate results asynchronously as they become available. Unfortunately this is not yet supported in Internet Explorer, but it is planned for IE10, and other browsers already support it.
As suggested by cdhowie and Jonathan M, another solution (inferior, but supported everywhere) is to use setTimeout to pause your code occasionally and let the browser respond to events. You would need to make your code somewhat more complicated to make this work. To give you an idea, to pause every 1000 items you would do something like this:
var workSliceSize = 1000;
var doWorkFromIndex = function(start) {
for (var i = start; i < norms.length; i++) {
if (i - start > workSliceSize) {
setTimeout(0, doWorkFromIndex, i + 1);
break;
}
normals.push... // your code here
}
}
doWorkFromIndex(0);
Try setTimeout(). It processes code asynchronously. I've used it to free up the screen processing by doing:
setTimeout(function() {
// the stuff I want to accomplish while keeping the gif going
},
0
);
Here I've set the timeout time period at zero milliseconds, but it can be whatever you want.
Try
var i = 0;
function processNormals() {
normals.push(toAscii(norms[i], mins[5], maxes[5])); //nx
normals.push(toAscii(norms[i+1], mins[6], maxes[6]));//ny
normals.push(toAscii(norms[i+2], mins[7], maxes[7]));//nz
i = i+3; //skip next 2 as they're already converted
percentComplete = (normals.length/norms.length)*100;
percentComplete = Math.round(percentComplete);
document.getElementById('loadingScrn').innerHTML = "Processing " +percentComplete + "%" + " Complete";
if(i < norms.length) setTimeout(processNormals, 20);
}

Categories