I saw a lot of questions about how to put delay or sleep in javascript. I tried everything i just could not have the code work, like i wanted to.
So i get like 500 friend requests on facebook, kinda annoying accepting one them one by one. So i found the script which does accept all my friend requests at once. I tried the set time out and sleep codes, which actually do the work but i want to put the delay between each actions. With Set timeout, it just delays the whole script. So i wanna do like this:
Confirm
1sec delay
Confirm
1sec delay
confirm
1sec deleay...
Here is my original script:
var field = document.getElementsByName("actions[accept]");
for (i = 0; i < field.length; i++)
field[i].click() ;
Here is another solution for this, we need to call immediate function with "i" param to get it correct into setTimeout:
var field = document.getElementsByName("actions[accept]");
for (var i = 0; i < field.length; i++) {
(function(i){
setTimeout(function() {
field[i].click();
}, i * 1000);
})(i);
}
But I suggest to use recursion answer above as better solution for this, because good practise not to use functions in loop. Here is reference :
https://jslinterrors.com/dont-make-functions-within-a-loop
The correct solution to your problem would be the following:
var field = document.getElementsByName("actions[accept]");
var index = 0;
function acceptFriends(){
if(index<field.length){
field[index].click();
index++;
setTimeout(acceptFriends,1000);
}
}
acceptFriends();
var field = document.getElementsByName("actions[accept]");
for (i = 0; i < field.length; i++){
setTimeout(function(){field[i].click()},(i*1000))
}
Related
I have succeeded in cobbling together pieces of code that achieve my goal. However, I would like some advice from more advanced vanilla JS programmers on how I can go about reaching my goal in a better way.
To start, I want to introduce my problem. I have a piece of text on my website where a portion is designed to change every so often. For this, I am running through a loop of phrases. To run this loop continuously, I first call the loop, then I call it again with setInterval timed to start when the initial loop ends. Here is the code I've got, which works even if it isn't what could be considered quality code:
function loop(){
for (let i = 0; i < header_phrases.length; i++){
(function (i) {
setTimeout(function(){
header_txt.textContent = header_phrases[i];
}, 3000 * i);
})(i);
};
}
loop();
setInterval(loop, 21000);
Is there a better way to right this code for both performance and quality? Do I need to use async? If so, any material I can see to learn more? Thanks!
You can implement the same logic using recursion.
function recursify(phrases, index = 0) {
header_txt.textContent = phrases[index];
setTimeout(function () {
recursify(phrases, index < phrases.length - 1 ? index + 1 : 0);
}, 300)
}
recursify(header_phrases);
The function 'recursify' will call itself after 300 miliseconds, but everytime this function gets called, the value of index will be different.
If I understand your requirement correctly, you want top populate an element from an array of values.
A simple way to do this is:
doLoop();
function doLoop() {
var phraseNo=0;
setTimeout(next,21000);
next();
function next() {
header_txt.textContent = header_phrases[phraseNo++];
if(phraseNo>=header_phrases.length) phraseNo=0;
}
}
This simply puts the next() function on the queue and waits.
The call to next() before the function simply starts it off without waiting for the timeout.
this is assuming that header_txt and header_phrases are not global vars. using global vars isn't a good idea.
var repeatIn = 3000;
phraseUpdater();
function phraseUpdater() {
var updateCount = 0,
phrasesCount = header_phrases.length;
setHeader();
setTimeout(setHeader, repeatIn);
function setHeader() {
header_txt.textContent = header_phrases[updateCount++ % phrasesCount] || '';
}
}
I'd like to add a class to a series of spans using setTimeout() such that the class is added in cascading fashion, creating a visual progression rather than having them all set at once. I've tried so many different ways.
Here's a codepen...
http://codepen.io/geirman/pen/nDhpd
The codepen tries to mimic a working example I've found here...
https://developers.google.com/maps/documentation/javascript/examples/marker-animations-iteration
The problem is that I can't seem to delay the addClass successively, so it happens all at once. Here's the current code.
/* The Problem Code
********************/
var span$ = $("span");
var index = 0;
var factor = 500;
function colorSpans(){
for(var i = 0; i < span$.length; i++){
setTimeout(function(){
animate();
}, factor*index);
}
index = 0;
}
function animate(){
span$[index++].className = "lg";
}
colorSpans();
One more thing, I'd love to do this sans jQuery, but will accept a jQuery solution as well.
In your current code, it looks like the very first call to animate will execute immediately, after which index will be 1. But the loop is likely to finish executing before the second timeout-handler is called (i.e., the loop won't take 500ms to execute). So this means that the remaining spans will appear instantaneously since the value of index will not be updated.
Something like this should work:
function colorSpans() {
for(var i = 0; i < span$.length; i++) {
(function(i) {
setTimeout(function() {
animate(i);
}, factor * i);
})(i); //force a new scope so that the "i" passed to animate()
//is the value of "i" at that current iteration and not
//the value of "i" when the loop is done executing.
}
}
function animate(i) {
span$[i].className = "lg";
}
The above code is also preferable because you're not using a global variable to maintain state between colorSpans and animate (i.e., we're not using index).
But you can make additional improvements by using setInterval instead:
var i = 0;
var intervalId = setInterval(function() {
if(i < span$.length) {
animate(i);
i++;
} else {
clearInterval(intervalId);
}
}, 500)
I think this is cleaner than the setTimeout approach.
Check out the updated codepens:
setTimeout approach
setInterval approach
Is that possible to set delay between the javascript for loop NOT using only settimeout but based on when an specific ajax call is done?
something like:
for (var i = 0 ; i < list.length ; i++)
{
$when('index.jsp').done(function(a1){
alert(i);
});
}
Say ajax request is sent and the first alert comes up, then the second iteration and alert is performed when the first ajax call is done.
I think the following should work:
(function() {
var i = 0, end = list.length;
function iterate() {
$.ajax('index.jsp').done(function() {
alert(i);
i++;
if (i < end) {
iterate();
}
});
}
iterate();
})();
An easy way to accomplish what you're after would be to use a callback in the Ajax function to recursively call itself. Otherwise I'm not sure of a way to do it in a for loop because Ajax calls are asynchronous.
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
This question already has answers here:
All the setTimeouts inside javascript for loop happen at once
(2 answers)
Closed 10 years ago.
I searched around and found some others with similar issues, but I can't seem to find a solution or clear explanation.
var content = 'test<br />';
for( var i = 1; i < 6; i++ ) {
setTimeout(function() {
document.write(content);
}, 3000);
}
I'd like the code in the for loop to execute 5 times, with a three second delay between each loop. When it runs it, at least on the surface, looks like a three second delay at page load, then goes through all the loops with no delay.
What am I missing?
Your problem is that all the calls are happening after 3000 ms. Do perform each call 3s apart do this:
var content = 'test<br />';
for( var i = 1; i < 6; i++ ) {
setTimeout(function() {
document.write(content);
}, 3000 * i);
}
You probably need to use setInterval ('cause you're trying to run code at a certain "interval")
// first create an isolated namespace because we don't need to dirty the global ns //
(function(){
var counter = 0;
var maxIterations = 6;
var intervalReference = setInterval(function(){
// your code goes here //
alert('test');
// the stop condition //
++counter;
if (counter == maxIterations) {
clearInterval(intervalReference);
}
}, 3000);
}())
setInterval is probably the way to go (see Alin's answer) but if you were wanting to go down the setTimeout route, the code would look something like this:
var loop = 0;
var content = "test<br>";
function startTimeout(init){
if(init!==true){
document.write(content);
loop++;
}
if(loop<5){
setTimeout(startTimeout, 3000);
}
}
startTimeout(true);