So far, this(code below) allows me to delay my function that displays a response for 4 seconds after I click a button. Now I would like to know how I would reset this timout so that I can re-click the button for the delay to occur.
js:
var ball = document.getElementById("ball");
function start(){
console.log("start");
ball.classList.toggle("shake");
};
ball.addEventListener("click", start);
function newResponse() {
var question = document.getElementById("question").value;
var response = ["..."];
for (var i = 0; i < question.length; i++) {
var randomResponse = Math.floor(Math.random() * response.length);
document.getElementById('quoteDisplay').innerHTML = response[randomResponse];
}
}
ball.addEventListener("click", newResponse)
Declare the function outside the timeout so you can reference it multiple times. Then, call setTimeout with the function, assign the result to a variable, and if you want to clear the timeout, call clearTimeout on that variable:
function newResponse() {
var question = document.getElementById("question").value;
var response = ["..."];
for (var i = 0; i < question.length; i++) {
var randomResponse = Math.floor(Math.random() * response.length);
document.getElementById('quoteDisplay').innerHTML =
response[randomResponse];
}
}
// Set the timeout once:
let timeout = setTimeout(newResponse,4000);
// Clear it:
clearTimeout(timeout);
// Set the timeout again:
timeout = setTimeout(newResponse,4000);
// etc
$('#test').click(function(){
var timer = setTimeout(function newResponse() {
alert('Click answered...');
clearTimeout(timer);
},4000)
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="button" value="test" id='test'/>
You can do like this
var timer = setTimeout(function newResponse() {
var question = document.getElementById("question").value;
var response = ["..."];
for (var i = 0; i < question.length; i++) {
var randomResponse = Math.floor(Math.random() * response.length);
document.getElementById('quoteDisplay').innerHTML =
response[randomResponse];
}
clearTimeout(timer);
},4000)
I am using google maps and i am trying to put a pause in execution to prevent QUERY_LIMIT usage issue. My function that plots the addresses looks like this.
The code works, however i want to try setTimeout or setInterval to see if its going to look better on UI.
How do i call it, what should be the first argument?
Thanx alot.
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
//geocode "free" usage limit is 5 requests per second
//setTimeout(PlotAddressesAsUnAssigned, 1000);
//sleep(500);
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
Doing a pause (asynchronous execution) inside a loop (synchronous) will usually result in a lot of trouble.
You can use recursive calls that are done only when a timeout ends.
var vLocations = [];
// Manages the timeout and recursive calls
function AddWaypointAndUnassignedWithPause(index){
setTimeout(function(){
// When the timeout expires, we process the data, and start the next timeout
AddWaypointAndUnassigned(vAddresses[index]);
// Some other code you want to execute
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
if(index < vAddresses.length-1)
AddWaypointAndUnassignedWithPause(++index);
}, 1000);
}
// Start the loop
AddWaypointAndUnassignedWithPause(0);
JSFiddle example.
Try this, hope this will help
vLocations = [];
for (var i = 0; i < vAddresses.length; i++) {
//pause to prevent OVER_QUERY_LIMIT issue
setTimeout(function(){
//this will resolve the address and store it in vLocations
AddWaypointAndUnassigned(vAddresses[i]);
}, 500);
var z = i % 4;
if (z==0 && i != 0) {
//sleep after every 5th geocode call
//alert('going to sleep...i: ' + i);
//sleep(3000);
}
}
What about a waiting line, thats fired when an item is added and stopped when there are no items left.
With setTimeout:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startTimeout();
}
function startTimeout() {
if( to === null ) {
to = setTimout(processLocation, INTERVAL);
}
}
function processLocation() {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
to = setTimout(processLocation, INTERVAL);
} else {
to = null;
}
}
With setInterval:
var INTERVAL = 1000 / 5;
var to = null;
var vLocations = [];
function addAddress(vAddress) {
vLocations.push(vAddress);
startInterval();
}
function startInterval() {
if( to === null ) {
to = setInterval(processLocation, INTERVAL);
}
}
function processLocation(cb) {
if( vLocations.length ) {
var vAddress = vLocations.shift();
AddWaypointAndUnassigned(vAddress);
} else
clearInterval(to);
to = null;
}
}
I have a progress bar that I update in a loop of many iterations.
https://jsfiddle.net/k29qy0do/32/
(open the console before you click the start button)
var progressbar = {};
$(function () {
progressbar = {
/** initial progress */
progress: 0,
/** maximum width of progressbar */
progress_max: 0,
/** The inner element of the progressbar (filled box). */
$progress_bar: $('#progressbar'),
/** Set the progressbar */
set: function (num) {
if (this.progress_max && num) {
this.progress = num / this.progress_max * 100;
console.log('percent: ' + this.progress + '% - ' + num + '/' + this.progress_max);
this.$progress_bar.width(String(this.progress) + '%');
}
},
fn_wrap: function (num) {
setTimeout(function() {
this.set(num);
}, 0);
}
};
});
$('#start_button').on('click', function () {
var iterations = 1000000000;
progressbar.progress_max = iterations;
var loop = function () {
for (var i = 1; i <= iterations; i++) {
if (iterations % i === 100) {
progressbar.set(i); //only updates the progressbar in the last iteration
//progressbar.fn_wrap(i); //even worse, since no output to the console is produced
}
}
}
//setTimeout(loop, 0);
loop();
});
The console is updated iteratively as expected.
However, the progressbar is not updating.
The problem is that the browser window seems to 'hang' until the loop finishes.
Only the console is updated, not the progressbar.
I have tried to add the setTimeout, as suggested below, in several places.
But that just makes things worse, because I then do not even get the console to output the progress while executing the loop.
Okay, I found a solution in the answer to this question:
Javascript: How to update a progress bar in a 'for' loop
var i = 0;
(function loop() {
i++;
if (iterations % i === 100) {
progressbar.set(i); //updates the progressbar, even in loop
}
if (i < iterations) {
setTimeout(loop, 0);
}
})();
My solution:
https://jsfiddle.net/ccvs4rer/3/
Lets break this down to steps
Step 1: Clean up HTML
Assuming the purpose of your question is to understand how to work the progress bar and not the styles or the labels (loading, please be patient, etc.). Lets just have the progress bar and the start button.
<div id='progressbar-outer' style="">
<div id='progressbar' style=""></div>
</div>
<button id="start_button">Start</button>
Step 2: The Styles
Lets make the progress bar visible to the user
#progressbar-outer {
height:2em;
border:5px solid #000;
width:15em;
}
#progressbar {
width:0%;
background-color:#F00;
height:100%;
}
Step 3: Using setTimeout where it belongs
In your code, you have used setTimeout to set the value of your progress bar. However, the for loop is still active.
for (var i = 1; i <= iterations; i++) {
if (iterations % i === 100) {
progressbar.set(i); //only updates the progressbar in the last iteration
//progressbar.fn_wrap(i); //even worse, since no output to the console is produced
//setTimeout(function() {
// progressbar.set(i);
//}, 0);
}
}
The use of setTimeout does not affect the rest of the code. Hence, the UI was held hostage till the loop ended. Try the following code.
$('#start_button').on('click', function () {
var iterations = 100;
progressbar.progress_max = iterations;
var loop = function (value) {
progressbar.set(value);
if (value < iterations) setTimeout(function () {
loop(value + 1)
}, 30);
else $('#progressbar').css('background-color', '#0F0');
}
loop(1);
});
Preview
Try this fiddle: https://jsfiddle.net/Ljc3b6rn/4/
What you really want is an Asynchronous loop to allow the browser to update the DOM in between iterations.
JSFiddle: http://jsfiddle.net/u5b6gr1w/
function delayedLoop(collection, delay, callback, context) {
context = context || null;
var i = 0,
nextInteration = function() {
if (i === collection.length) {
return;
}
callback.call(context, collection[i], i);
i++;
setTimeout(nextInteration, delay);
};
nextInteration();
}
Some HTML:
<div class="progress-bar"><div style="width: 0"></div></div>
A splash of CSS:
.progress-bar {
border: 1px solid black;
background-color: #f0f0f0;
}
.progress-bar div {
background-color: red;
height: 1.25em;
}
And some JavaScript to wire things together:
var progressBar = document.querySelector(".progress-bar div"),
items = [1,2,3,4,5,6,7,8,9,10];
delayedLoop(items, 500, function(item, index) {
var width = (item / items.length * 100) + "%";
progressBar.style.width = width;
progressBar.innerHTML = width;
});
My guess would be that all your progress updates are running in the same call stack. While JavaScript code is running, the DOM cannot update. Maybe this question will help you come up with a work-around.
What do you wnat to do? Why do you need it? You should only use a progressbar when you have to wait for something to finish. But we don't know what you do on your page.
If you want to display the progress of an ajax upload:
$.ajax({
...
xhr: function() {
var xhr = $.ajaxSettings.xhr();
$(xhr.upload).bind("progress", function(event) {
var e = event.originalEvent;
var percent = 0;
if (e.lengthComputable)
percent = Math.ceil(e.loaded/e.total*100);
$("#progress").width(percent+"%");
});
return xhr;
}
...
});
For images, you need an ajax call:
$.ajax({
method: "GET",
url: "http://example.com/path/image.jpg",
xhr: function() {/* see the code above*/ }
...
});
For getting the content of an uploaded file:
var reader = new FileReader();
reader.readAsText(uploadedFile);
$(reader).bind("progress", function(e) {
var percent = 0;
if (e.lengthComputable)
percent = Math.ceil(e.loaded/e.total*100);
$("#progress").css("width", percent+"%");
});
For large around of process, like math or appending a lot of divs that will take 10+ secons:
Main.js:
var worker = new Worker("Worker.js");
$(worker).bind("message", function(data) {
$("#progress").width((data*100)+"%");
});
Worker.js:
var total = 43483,
finished = 0,
doStuff = function() {
++finished;
return 1+1;
};
setInterval(function()
{
self.postMessage(finished/total);
}, 100);
for (var i = 0; i < total; ++i)
setTimeout(doStuff, i*10);
Because it's nice, and you want to tell the user there's a progress when there isn't, just animate the div:
$("#progress").animate({width: "100%"}, 3000);
You can use promises to wait until the width is set before continuing the loop.
Updating the progress bar for 1000000000 iterations will be slow if you go 1 by 1, so you might find it useful to decrease the update frequency.
Instead of a for loop, I used a recursive function that loops when the promise has been fulfilled.
set: function (num) {
var deferred = $.Deferred();
if (this.progress_max && num) {
this.progress = num / this.progress_max * 100;
var self = this;
self.$progress_bar.animate({"width": String(this.progress) + '%'}, "fast", function() {
deferred.resolve();
});
return deferred;
}
}
$('#start_button').on('click', function () {
var iterations = 1000000000;
var i = 0;
progressbar.progress_max = iterations;
var loop = function(){
i+=100000000;
if(i <= iterations){
progressbar.set(i).then(function(){
loop();
}); ;
}
};
loop();
});
https://jsfiddle.net/k29qy0do/34/
You have to use window.requestAnimationFrame, otherwise the browser will block until your loop is finished. The callback passed to requestAnimationFrame will get a timestamp as a parameter which you might be able to use for calculations of the progress.
This are my 2 takes on the question:
Using a web worker. The webworker blob code comes from here
Web worker code:
<script type="text/ww">
function loop(e) {
var data = JSON.parse(e.data);
var i = parseInt(data.i, 10);
var iterations = parseInt(data.iterations, 10);
while (iterations % ++i !== 100 && i <= iterations);
if(i <= iterations) {
self.postMessage(JSON.stringify({ i: i, iterations: iterations }));
}
}
self.onmessage = function(e) {
loop(e);
};
</script>
The code:
var ww = document.querySelector('script[type="text/ww"]'),
code = ww.textContent,
blob = new Blob([code], {type: 'text/javascript'}),
blobUrl = URL.createObjectURL(blob),
worker = new Worker(blobUrl);
worker.onmessage = function(e) {
var data = JSON.parse(e.data);
var i = parseInt(data.i, 10);
var iterations = parseInt(data.iterations, 10);
progressbar.set(i);
worker.postMessage(JSON.stringify({ i: i, iterations: iterations }));
}
$('#start_button').on('click', function () {
var iterations = 1000000000;
progressbar.progress_max = iterations;
worker.postMessage(JSON.stringify({ i: 0, iterations: iterations }));
});
The other idea hangs the UI thread, but changes the width visually, as I use requestAnimationFrame to break the counting, change width of the progressbar, and then continue the count.
function loopFrame(i, iterations) {
requestAnimationFrame(function() {
if (iterations % i === 100) {
progressbar.set(i);
}
if(i < iterations) {
loopFrame(i + 1, iterations);
}
});
}
$('#start_button').on('click', function () {
var iterations = 1000000000;
console.log(iterations);
progressbar.progress_max = iterations;
loopFrame(0, iterations);
});
Maybe this will be usefull.
var service = new Object();
//function with interrupt for show progress of operations
service.progressWhile = new Object();
service.progressWhile.dTime = 50; //step ms between callback display function
service.progressWhile.i = 0; //index
service.progressWhile.timer = 0; //start time for cycle
//#parametr arr - array for actions
//#parametr actionCallback - The function for processing array's elements
//#parametr progressCallback - function to display the array index
function progressWhile(arr, actionCallback, progressCallback) {
try {
var d = new Date();
service.progressWhile.timer = d.getTime();
log(service.progressWhile.i);
if (service.progressWhile.i >= arr.length) {
service.progressWhile.i = 0;
return;
}
while (service.progressWhile.i < arr.length) {
actionCallback(arr[service.progressWhile.i++]);
d = new Date();
if (d.getTime() - service.progressWhile.timer > service.progressWhile.dTime) {
break;
}
}
if (progressCallback != undefined)
progressCallback(service.progressWhile.i);
} catch (er) {
log(er);
return;
}
setTimeout(function () {
progressWhile(arr, actionCallback, progressCallback);
}, 0);
}
Here's updated fiddle
I used animate to make it a progress bar like look and feel.
Hope this will help you.
var progressbar = {};
$(function() {
progressbar = {
/** initial progress */
progress : 0,
/** maximum width of progressbar */
progress_max : 0,
/** The inner element of the progressbar (filled box). */
$progress_bar : $('#progressbar'),
/** Method to set the progressbar.*/
set : function(num) {
if (this.progress_max && num) {
this.progress = num / this.progress_max * 100;
console.log('percent: ' + this.progress + '% - ' + num + '/' + this.progress_max);
$('#progressbar').animate({
width : String(this.progress) + '%',
}, 500, function() {
// Animation complete.
});
}
},
fn_wrap : function(num) {
setTimeout(function() {
this.set(num);
}, 0);
}
};
});
$('#start_button').on('click', function() {
$('#progressbar').css('width', '0%');
var iterations = 1000000000;
progressbar.progress_max = iterations;
var loop = function() {
for (var i = 1; i <= iterations; i++) {
if (iterations % i === 100) {
progressbar.set(i);
//only updates the progressbar in the last iteration
}
}
}
loop();
});
Fiddler
[1]: https://jsfiddle.net/k29qy0do/21/
I am using the Peity js plugin to create donut charts on my page. I am trying to animate the chart for each of the .foo elements:
<span class="foo" data-value="10"></span>
$('.foo').each(function () {
var updateChart = $(this).peity('donut');
var text = "";
var i = 0;
function myLoop() {
setTimeout(function () {
text = i + "/12";
updateChart.text(text)
.change()
i = i + 0.2;
var maxValue = $(this).data("value");
if (i <= maxValue) myLoop();
}, 0.5)
}
myLoop();
});
However it won't work for some reason with no errors in console. If I remove the $('.foo').each(function () { ... } part (and all "this" instances) the code will work. Thanks in advance for any help.
The problem is the context inside the timer handler, the easiest fix here is to use a closure variable
$('.foo').each(function () {
var $this = $(this);
var updateChart = $this.peity('donut');
var text = "";
var i = 0;
function myLoop() {
setTimeout(function () {
text = i + "/12";
updateChart.text(text)
.change()
i = i + 0.2;
var maxValue = $this.data("value");
if (i <= maxValue) myLoop();
}, 0.5)
}
myLoop();
});
When the timeout callback is executed, the this context refer to window, because you are actually calling window.setTimeout method.
Try this:
$('.foo').each(function () {
var updateChart = $(this).peity('donut');
var text = "";
var i = 0;
function myLoop() {
setTimeout($.proxy(function () {
text = i + "/12";
updateChart.text(text)
.change()
i = i + 0.2;
var maxValue = $(this).data("value");
if (i <= maxValue) myLoop();
},this), 0.5)
}
myLoop();
});
I want to return a value inside a setInterval. I just want to execute something with time interval and here's what I've tried:
function git(limit) {
var i = 0;
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
return 'done';
}
i++;
}, 800);
}
var x = git(5);
console.log(x);
And it's not working.
Is there any other way?
What I'm going to do with this is to do an animation for specific time interval. Then when i reached the limit (ex. 5x blink by $().fadeOut().fadeIn()), I want to return a value.
This is the application:
function func_a(limit) {
var i = 0;
var defer = $.Deferred();
var x = setInterval(function () {
$('#output').append('A Running Function ' + i + '<br />');
if (i == limit) {
$('#output').append('A Done Function A:' + i + '<br /><br />');
clearInterval(x);
defer.resolve('B');
}
i++;
}, 500);
return defer;
}
function func_b(limit) {
var c = 0;
var defer = $.Deferred();
var y = setInterval(function () {
$('#output').append('B Running Function ' + c + '<br />');
if (c == limit) {
$('#output').append('B Done Function B:' + c + '<br /><br />');
clearInterval(y);
defer.resolve('A');
}
c++;
}, 500);
return defer;
}
func_a(3).then( func_b(5) ).then( func_a(2) );
This is not functioning well, it should print A,A,A,Done A,B,B,B,B,B,Done B,A,A,Done A but here it is scrambled and seems the defer runs all function not one after the other but simultaneously. That's why I asked this question because I want to return return defer; inside my if...
if (i == limit) {
$('#output').append('A Done Function A:' + i + '<br /><br />');
clearInterval(x);
defer.resolve('B');
// planning to put return here instead below but this is not working
return defer;
}
Do you expect it to wait until the interval ends? That would be a real pain for the runtime, you would block the whole page. Lots of thing in JS are asynchronous these days so you have to use callback, promise or something like that:
function git(limit, callback) {
var i = 0;
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
callback('done');
}
i++;
}, 800);
}
git(5, function (x) {
console.log(x);
});
Using a promise it would look like this:
function git(limit, callback) {
var i = 0;
return new Promise(function (resolve) {
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
resolve('done');
}
i++;
}, 800);
});
}
git(5)
.then(function (x) {
console.log(x);
return new Promise(function (resolve) {
setTimeout(function () { resolve("hello"); }, 1000);
});
})
.then(function (y) {
console.log(y); // "hello" after 1000 milliseconds
});
Edit: Added pseudo-example for promise creation
Edit 2: Using two promises
Edit 3: Fix promise.resolve
Try to get a callback to your git function.
function git(limit,callback) {
var i = 0;
var git = setInterval(function () {
console.log(i);
if (i === limit - 1) {
clearInterval(git);
callback('done') // now call the callback function with 'done'
}
i++;
}, 800);
}
var x = git(5,console.log); // you passed the function you want to execute in second paramenter