I know maybe this is not possible.
I have searched the web but without suceess
I have a while loop and I want to do two things:
Update a textarea with formatted information
Update the width of div (a progress bar).
The first function has 4-5 additional sub-functions.
Basically I have a 6 elements numerical array. I have a custom format function to create a formatted string elements for my numbers. etc.
if (reg_index/reg_total > last_refresh) {
window.setTimeout (
function() {
document.getElementById("progress_line").style.width = "" + 100 * last_refresh + "px";
document.getElementById("progress_value").innerHTML = my_format( 100*last_refreh, "###" ) + "%";
},
5
);
last_refresh+=0.01;
}
Ok, I'm unable to define a right timeout interval to get what I want.
Can anybody point me to a useful link?
Thanks.
The problem you're having here is that all of your functions will use the last value of last_refresh. When you define a function, it has an enduring reference to the variables in scope, not a copy of their values when it's created. More: Closures are not complicated
You could do this:
function update() {
if (reg_index/reg_total>last_refresh)
document.getElementById("progress_line").style.width =" "+100*last_refresh+"px";
document.getElementById("progress_value").innerHTML=my_format(100*last_refreh,"###")+"%";
last_refresh+=0.01;
setTimeout(update, 0); // Or 5 or whatever
}
}
update();
That uses a function that does a bit of the work, then schedules itself to do more of the work in a moment. It closes over the reg_index, reg_total, and last_refresh variables.
If you know it needs to run at least once, you can make it slightly more efficient:
function update() {
document.getElementById("progress_line").style.width =" "+100*last_refresh+"px";
document.getElementById("progress_value").innerHTML=my_format(100*last_refreh,"###")+"%";
last_refresh+=0.01;
if (reg_index/reg_total>last_refresh)
setTimeout(update, 0); // Or 5 or whatever
}
}
update();
To help with the concept of looping with setTimeout, compare this standard loop (the browser doesn't update until the very end): Live Copy | Live Source
var a = ["zero", "one", "two", "three", "four", "five"];
var index;
for (index = 0; index < a.length; ++index) {
display("Entry " + index + " is " + a[index]);
}
...with this equivalent using setTimeout, which yields to the browser on every iteration so it can update (and even if the interval is 0, it runs much more slowly — I've used 200 here so you can see it run): Live Copy | Live Source
var a = ["zero", "one", "two", "three", "four", "five"];
var index;
index = 0;
update();
function update() {
display("Entry " + index + " is " + a[index]);
++index;
if (index < a.length) {
setTimeout(update, 200);
}
}
Below you've said:
Unfortanetly I cant apply at my example.... I have a main loop with 2000 reads on local database.... I have done it works using timeouts but now my code spends a lot of time
I usually deal with that by breaking things up in to chunks (or see below for another alternative), example: Live Copy | Live Source
var index;
var total = 10000; // 10,000 to do in total
index = 0;
update();
function update() {
var limit = Math.min(index + 100, total); // Do them 100 at at time
while (index < limit) {
if (index % 10 == 0) {
display("Process entry #" + index);
}
++index;
}
if (limit < total) {
setTimeout(update, 200);
}
}
Choose the size of the chunks so that you're updating (yield) often enough, but not updating (yielding) so much that you lose too much time. The above does it based on the number of loops, but another way is to let yourself run for (say) one full second and then yield. You can get a lot of work done in a second.
The other alternative is to use web workers, at least on the browsers that support them. My other answer here on Stack Overflow has a discussion and example.
Even if you use web workers, though, you'll probably want to break the work into chunks, because if you have 2,000 records to get through, it doesn't make sense to update the progress bar 2,000 times &mdasdh; you'll just do updates a human can't readily perceive. 100 updates would be more than enough, probably even just 20 (so, 100 records/chunk) would be fine.
Related
I may be approaching this in completely the wrong way, as I'm a bit of a novice when it comes to javascript, but essentially what I'm trying to do (for a bit of fun) is to have a cell of a table change colour onclick, then have the cells to the north, east, south, and west change to a different colour in sequence (think of the bomb effect in bomberman).
So far, I've managed to get the south direction to work using the following code:
function timeOutDown(i) {
let bubbleRowPositive = col + rowArray[i];
let bubbleRowPositiveId = document.getElementById(bubbleRowPositive);
setTimeout(function() {
bubbleRowPositiveId.style.backgroundColor = "#AA3333";
},i * 50);
}
for (let i=row; i<colArray.length; i++) {
timeOutDown(i);
}
For context, there are 15 rows and 15 columns of evenly sized table cells, with ID's like "a1" and "h14".
However, the issue I'm coming across is that the inverse will still iterate upwards, even when reversing the for loop and I can't figure out why:
function timeOutUp(k) {
let bubbleRowNegative = col + rowArray[k];
let bubbleRowNegativeId = document.getElementById(bubbleRowNegative);
setTimeout(function() {
bubbleRowNegativeId.style.backgroundColor = "#AA3333";
console.log(`Index: ${k}, Row Num: ${rowArray[k]}`);
},k * 50);
}
for (let k=row-2; k>=0; k--) {
timeOutUp(k);
console.log(k);
}
I had started by using the same function to handle both for loops, that didn't work, so I attempted this method using 2 separate functions.
Is there an easier way to go about this?
Even though your for loop reversed, it still passes in the same index for every iteration, and that index is used to compute the delay it gets. So no matter what, the item at index 0 gets 0*50 milliseconds delay, regardless whether it happens first or last. You still need your original counter in order to define their ordered index. You could solve it like this:
function timeOutUp(k, i) {
let bubbleRowNegative = col + rowArray[k];
let bubbleRowNegativeId = document.getElementById(bubbleRowNegative);
setTimeout(function() {
bubbleRowNegativeId.style.backgroundColor = "#AA3333";
console.log(`Index: ${k}, Row Num: ${rowArray[k]}`);
},i * 50);
}
for (let k=row-2, i = 0; k>=0; k--, i++ ) {
timeOutUp(k, i);
console.log(k, i);
}
I just added 1 variable: i back in, that counts up. It gets passed to timeOutUp to compute the actual delay in the order you intend.
With the below code:
$('#button').on('click', function () {
var longArray = searchArray; // assume this has 100 or more postalcodes init
var shortArrays = [], i, len;
for (i = 0, len = longArray.length; i < len; i += 100) {
shortArrays.push(longArray.slice(i, i + 100));
}
// Now we iterate over shortArrays which is an array of arrays where each array has 100 or fewer
// of the original postalcodes in it
for (i = 0, len = shortArrays.length; i < len; i++) {
// shortArrays[i] is an array of postalcodes of 100 or less
$.each(shortArrays[i], function(index, value){
setTimeout( function() {
// Each parent gets its own searchToggle class
$('.postcodes input[data-postcode*="' + value + '"]').parent().parent().addClass('searchToggle');
// Each parent also gets a data filter attribute for ordering the results
$('.postcodes input[data-postcode*="' + value + '"]').parent().parent().attr('data-filter' , index);
// We display the items in the search array
$('.postcodes input[data-postcode*="' + value + '"]').parent().parent().css('display', 'flex');
$('.postcodes .searchToggle .postcode input[data-postcode*="' + value + '"]').parent().css('display', 'flex');
}, 0 );
})
} // /for
alert('Finished message');
});
I try to show an alert message(for debugging) once the $.each() is finished. Since this each goes through an array that could be 1000s of postal codes long I broke it up in chunks of 100. This to prevent the dreaded browser is unresponsive. This is all working fine but the alert fires immediately on click.
I have tried several things already:
I tried by using a count: ABOVE THE EACH var count = 0; INSIDE THE EACH count++ if ( count == longArray.length ) { ALERT } But this also fired the alert immediately???
I tried it by using an interval but that became a mess almost instantly.
I tried a couple of other SO answers but all of them resulted in the alert to fire immediately.
When looking through the jQuery docs and previous codes that I have written it should just run the code after the each is finished but in this case it does not.
Any idea on why this is and how I can fix it.
PS: This alert could be other codes! Like sorting the results or something else.
PS2: I can change all the js/jQuery you see but I cannot change any of the HTML selectors.
PS3: Thank you for thinking about this issue and especially for commenting/answering!
I have solved it by adding another setTimeout.
So I replaced the alert (see Question) from
alert(Finished message);
To
setTimeout( function() {
// As an example I used alert in my question and here in this answer.
// This can be offcourse anything else. I use it for instance to sort the results.
alert(Finished message);
}, 0 );
This works for me, but it might not be the best way to deal with it. So I am still looking forward to what more experienced people think about the question or the answer.
I am making a chrome extension for web automation.The first step is to get a list of sites and instructions from a server in a delimiter-format.
Once the list is obtained it is divided into an array that i call "siteArray".
The site array is then divide into another array i call "instructionsArray"
and among those items in the array one of them is the duration spent on the site i.e instructionsArray[5] has the value of "10" seconds. {the duration is not the same for all the sites}
My question arises in how to delay(implementing duration)
One implementation I found is using a sleep function which turns out to be inefficient as it is just a long for loop
see code:
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
and then:
getlist();
for each(siteArray){
instructionsArray = siteArray[count].split(delimiter);
gotoUrl(instructionsArray[1]);
sleep(instructionsArray[5]);
count++;
}
Where
getlist() fetches a list of instructions and splits into the
siteArray .
gotoUrl() changes url.
sleep() runs the sleep function.
Is there a better way to implement a duration/wait/delay.
Rework the way you iterate over the array: process one entry, set a timer, repeat.
var count = 0;
processSite();
function processSite() {
if (count >= siteArray.length) {
console.log('all done!');
return;
}
var instructionsArray = siteArray[count].split(delimiter);
count++;
gotoUrl(instructionsArray[1]);
setTimeout(processSite, instructionsArray[5]);
}
After giving much thought into it I have solved the issue.
Using wOxxOm's idea I came up with this 'sub-idea' to create multiple timeouts i.e
for(index = 1;index<(siteArray.length);index++){
instructionsArray = siteArray[index].split(delimiter);
bigtime+=instructionsArray[5];
setTimeout(function(){processNext(count)},bigtime);
}
This code creates multiple timeout functions for each site.
similar to :
setTimeout(function(){processNext(count)},10 sec);
setTimeout(function(){processNext(count)},10+10 sec);
setTimeout(function(){processNext(count)},10+10+10 sec);
but in a dynamic way.
The count variable is change in the processNext function:
processNext(count){
instructionsArray = siteArray[count].split(delimiter);
visitUrl(instructionsArray[1]);
count++;
}
I give Thanks to wOxxOm for the insight.
I am trying to create a function that continuously adds the same number to itself. Or simply displays multiples of one number every so many seconds with the setInterval method.
For now, let's just say I want to display multiples of ten.
I know how to get a regular while loop to simply display multiples of ten in a row, but what I want to do here is continually replace the previous text every time the function is called. I am trying to create a game and this is going to be the experience calculator. So it needs to display the total experience earned over the given time.
I was trying something along the lines of this:
window.setInterval(
function writeExp ()
{
var j;
while (rounded > 0)
{
var i = w*10;
var j = j + i;
}
document.getElementByID("exp").innerHTML=j;
}, experience)
This seems logical enough to me, but obviously something is wrong, as it does not work. I have tried googling various things like how to sum numbers in javascript or continuously sum, among others, but it is somewhat difficult to word to get it more centered to my needs. So this is the best way to get my questions answered.
There are lot of undefineds in your code, but general example would be
var interval = 1000,
result = 0,
elem = document.getElementByID("exp");
window.setInterval(function () {
result *= 10;
elem.innerHTML = result;
}, interval);
or without global variables
var interval = 1000,
multiply = function (elem) {
var result = 0;
return function () {
result *= 10;
elem.innerHTML = result;
}
};
window.setInterval(multiply(document.getElementByID("exp")), interval);
So, here's my issue.
I need to write a script to be run in the console (or via Greasemonkey) to automate clicking of certain links to check their output.
Each time one of these links is clicked, they essentially generate an image in a flash container to the left. The goal here is to be able to automate this so that the QC technicians do not have to click each of these thumbnails themselves.
Needless to say, there needs to be a delay between each "click" event and the next so that the user can view the large image and make sure it is okay.
Here is my script thus far:
function pausecomp(ms) {
ms = ms + new Date().getTime();
while (new Date() < ms){}
}
var itemlist, totalnumber, i;
itemlist = document.getElementsByClassName("image");
totalnumber = parseInt(document.getElementById("quickNavImage").childNodes[3].firstChild.firstChild.nodeValue.replace(/[0-9]* of /, ""));
for(i = 0; i < totalnumber; i = i + 1) {
console.log(i);
itemlist[i].childNodes[1].click();
pausecomp(3000);
}
Now, totalnumber gets me the total number of thumbnails, obviously, and then itemlist is a list of get-able elements so I can access the link itself.
If I run itemlist[0].childNodes[1].click() it works just fine. Same with 1, 2, 3, etc. However, in the loop, it does nothing and it simply crashes both Firefox and IE. I don't need cross-browser capability, but I'm confused.
There is a built-in JS function "setInterval(afunction, interval)" that keeps executing a given function every "interval" miliseconds (1000 = 1s).
This fiddle shows how to use setTimeout to work through an array. Here is the code:
var my_array = ["a", "b", "c", "d"];
function step(index) {
console.log("value of my_array at " + index + ":", my_array[index]);
if (index < my_array.length - 1)
setTimeout(step, 3000, index + 1);
}
setTimeout(step, 3000, 0);
Every 3 seconds, you'll see on the console something like:
value of my_array at x: v
where x is the index in the array and v is the corresponding value.
The problem with your code is that your pausecomp loop is a form of busy waiting. Let's suppose you have 10 items to go through. Your code will click an item, spin for 3 seconds, click an item, spin for 3 seconds, etc. All your clicks are doing is queuing events to be dispatched. However, these events are not dispatched until your code finishes executing. It finishes executing after all the clicks are queued and (roughly) 30 seconds (in this hypothetical scenario) have elapsed. If the number of elements is greater that's even worse.
Using setTimeout like above allows the JavaScript virtual machine to regain control and allows dispatching events. The documentation on setTimeout is available here.
People were correct with SetInterval.
For the record, here's the completed code:
/*global console, document, clearInterval, setInterval*/
var itemlist, totalnumber, i, counter;
i = 0;
function findmepeterpan() {
"use strict";
console.log("Currently viewing " + (i + 1));
itemlist[i].scrollIntoView(true);
document.getElementById("headline").scrollIntoView(true);
itemlist[i].style.borderColor = "red";
itemlist[i].style.borderWidth = "thick";
itemlist[i].childNodes[1].click();
i = i + 1;
if (i === totalnumber) {
clearInterval(counter);
console.log("And we're done! Hope you enjoyed it!");
}
}
function keepitup() {
"use strict";
if (i !== 0) {
itemlist[i - 1].style.borderColor = "transparent";
itemlist[i - 1].style.borderWidth = "medium";
}
findmepeterpan();
}
itemlist = document.getElementsByClassName("image");
totalnumber = parseInt(document.getElementById("quickNavImage").childNodes[3].firstChild.firstChild.nodeValue.replace(/[0-9]* of /, ""), 10);
counter = setInterval(keepitup, 1500);