Running a jQuery function multiple times sequentially (for a Bookmarklet) - javascript

I've got the following jQuery code which I use in a Bookmarklet. It clicks on all the buttons on the page (with the class "Unfollow") one by one, with a random time between each one...
javascript: (function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
unfollow();
function unfollow() {
if (index >= 0) {
$(unfollowButtons[index--])
.click();
setTimeout(unfollow, Math.floor((Math.random() * 1000) + 500));
}
}
})();
I'd like to run the above function again twice once it has completed its cycle.
Just running the function again causes that to run in parallel with the first function call.
How do I run the unfollow() function 2 or 3 times without them all running in parallel?

Try it this way (using ES6 Promises):
var runUnfollow = function() {
return new Promise(function(resolve, reject){
var index = unfollowButtons.length - 1;
// fencepost for the loop
var p = Promise.resolve();
// we stop execution at `i == 0`
for (var i = index; i >= 0; i--) {
// run the promise
// then set `p` as the next one
p = p.then(unfollowTimeout.bind(null, i));
}
// make sure we run the last execution at `i == 0`.
p.then(function(){
resolve();
})
function unfollowTimeout(i){
// return a promise to run `unfollow` and a `setTimeout`
return new Promise(function(resolve,reject){
unfollow(i);
setTimeout(resolve, Math.floor((Math.random() * 1000) + 500));
})
}
function unfollow(i) {
$(unfollowButtons[i])
.click();
}
})
}
// run three times synchronously
runUnfollow().then(runUnfollow).then(runUnfollow).then(function(){
//finished
});
// another way to run three times synchronously
p = runUnfollow();
for(i=3; i > 0; i--){
p = p.then(runUnfollow);
}
p.then(function(){
//finished
});
// run in parallel
Promise.all([runUnfollow, runUnfollow, runUnfollow])
.then(function(){
//finished
});
EDIT: Went back and read your question again, realized you were trying to run everything multiple times. I've edited to reflect that.

Just reset index and restart after each button is clicked:
javascript: (function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
var totalRuns = 3;
unfollow();
function unfollow() {
if (index < 0 && totalRuns) {
totalRuns--;
unfollowButtons = $('button.Unfollow');
index = unfollowButtons.length - 1;
}
if (index >= 0) {
$(unfollowButtons[index--])
.click();
setTimeout(unfollow, Math.floor((Math.random() * 1000) + 500));
}
}
})();

You should look at Promises.
Resolve your Promise at your function's execution's very end and call your function again. You should be good with that.

In your specific case, you could simply build an array which contains twice each button :
// turn the jQuery selection into a regular array :
var unfollowButtons = $('button.Unfollow').get();
// build a new array, which contains two copies of the above selection :
unfollowButtons = unfollowButtons.concat(unfollowButtons);

You have 2 options
1.Use a User Script
For that, you need a User Script extension manager, for example Tampermonkey for Chrome, Greasemonkey for Firefox, etc.
But since you want a bookmarklet, just leave it.
2.Modify the Bookmarklet a little as follows
Add this code inside the unfollow function
That is check whether index reached 0 and also the flag is set or not.
FLAG is important otherwise it will create a infinitive recursion loop.
First set FLAG to 0 outside of unfollow function.
Then in unfollow function, if the FLAG is 0 and index is 0, initiate the next iteration and Set FLAG to 1.
if(index < 0 && FLAG==0){
var unfollowButtons = $('button.Unfollow');
FLAG=1;
var index = unfollowButtons.length - 1;
unfollow();
}
So, it will look like this.
javascript: (function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
var FLAG=0;
unfollow();
function unfollow() {
if (index >= 0) {
$(unfollowButtons[index--])
.click();
if(index < 0 && FLAG==0){
unfollowButtons = $('button.Unfollow');
FLAG=1;
index = unfollowButtons.length - 1;
unfollow();
}
setTimeout(unfollow, Math.floor((Math.random() * 1000) + 500));
}
}
})();
If you want to do it totally 3 times, change if(index < 0 && FLAG<=2){ and FLAG=1 to FLAG +=1

As i understand your requirements it can be done like this :
javascript: (function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
unfollow();
var runForNoOfTime = 3;
var runningForTime = 1;
function unfollow() {
if (index >= 0) {
$(unfollowButtons[index--]).click();
setTimeout(unfollow, Math.floor(Math.random() * 1000) + 500));
}else if(runningForTime < runForNoOfTime){
runningForTime++;
unfollowButtons = $('button.Unfollow'); //if buttons with class 'Unfollow' changes.
index = unfollowButtons.length - 1;
setTimeout(unfollow, Math.floor(Math.random() * 1000) + 500));
}
}
})();

You can use recursion to achieve the desired effect of running your function multiple times sequentially. Here's how this can be done:
(function() {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
function unfollow(callback) {
if (index >= 0) {
$(unfollowButtons[index--]).click();
}
callback();
}
function handleUnfollow(maxIter, iter) {
iter = typeof iter === "number" ? iter : 0;
if ( iter >= maxIter ) {
// base case reached, stop further recursive calls
return true;
}
// call unfollow once
unfollow(function() {
setTimeout(function() {
// recursive call
handleUnfollow(maxIter, ++iter);
}, Math.floor((Math.random() * 1000) + 500));
});
}
// execute recursive function, which will iterate 2 times
handleUnfollow(2);
})();

As far as I'm aware Javascript runs on a single thread, so there is no actual parallel processing taking place.
If you just simply want the function to run itself x times then use recursion:
function DoSomething(steps) {
// Do stuff here
steps--;
if (steps <== 0) {
return;
}
DoSomething(steps);
}
If you want things to run in "parallel" with Javascript then perhaps you could look into having some external code that manages threads and executes multiple Javascript processes in parallel (although I'm not sure if this is possible, and, if it is, whether you'll be able to have the scripts accessing the same data at the same time or talking to eachother).

I have made the current code as block and added wrapper logic. Check if this works.
(function() {
var iterations = 2;
unfollowBlock();
function unFollowBlock() {
if (iterations-- >0) {
var unfollowButtons = $('button.Unfollow');
var index = unfollowButtons.length - 1;
unfollow();
function unfollow() {
if (index >= 0) {
$(unfollowButtons[index--])
.click();
setTimeout(unfollow, Math.floor((Math.random() * 1000) + 500));
}
else { //index=-1 end of unfollowblock
setTimeout(unFollowBlock, Math.floor((Math.random() * 1000) + 500));
}
}
}
}
})();

Declare a flag/check variable eg. var isCycleComplete;
var isCycleComplete = false;
if(isCycleComplete){ // if true runs unfollow function twice
unfollow();
unfollow();
isCycleComplete = false; // resets flag
}
else{ // if false
unfollow();
isCycleComplete = true; //sets flag to true
}
M not a pro at javascript but See if this simple snippet helps you.

Related

Creating a variable counter in javascript with variable speed

I'm having a problem.
I want to make a counter that counts from 1 to 9 and repeats.
The time between the counts should be variable (1 to 10 seconds in the same series).
Sometimes, it should add 1, sometimes 2 (so sometimes skip one number).
Is that possible with javascript?
Thank you in advance.
This is the code I have, but is only counts, does not skip a number sometimes and the time to count is fixed at 500 ms.
<div id="value">1</div>
<script>
function animateValue(id){
var obj = document.getElementById(id);
var current = obj.innerHTML;
setInterval(function(){
obj.innerHTML = current++;
},500);
}
animateValue('value');
</script>
</html>````
First, a JSFiddle:
http://jsfiddle.net/5k0xsrj6/embedded/result/
JSFiddle with larger, stylized text:
https://jsfiddle.net/9f4vgLbx/embedded/result
Edit: I see you're not familiar with JavaScript. I've included non-ES6 JavaScript as well.
The biggest issue you'll face with your code is the use of setInterval, as you want a variable timer.
Instead of setInterval, consider a function that calls itself and sets a timer. Once the setTimeout is called, it will invoke the function again to set another timeout, effectively creating an interval.
Non ES6 Script:
var el = document.body;
var max_count = 9;
var current_count = 1;
// Function which sets our timer
function timer(delay) {
// Set a timeout with our passed `delay` arg
setTimeout(function () {
// Adds either 1 or 2 based on the return value of getIteration
current_count += getIteration();
// As we have a max, reset to 1 if we're over
if (current_count > max_count) {
current_count = 1;
}
// Update innerHTML
writer();
// Call next iteration
loop();
}, delay);
}
// Writes our innerHTML
function writer() {
el.innerHTML = current_count;
}
// Returns 1000 through 10000
function getDelay() {
return Math.ceil(Math.random() * 10) * 1000;
}
// Returns either 1 or 2
function getIteration() {
return Math.ceil(Math.random() * 2);
}
// Our main function to loop
function loop() {
// getDelay will return a value between 1000 - 10000
timer(getDelay());
}
// Sets Initial Value
writer();
// Main
loop();
Original:
Here's an example of the code on the JSFiddle. I've included comments to hopefully explain the logic.
{
const el = document.body;
const max_count = 9;
let current_count = 1;
// Function which sets our timer
const timer = delay => {
setTimeout(() => {
current_count += getIteration();
if (current_count > max_count) {
current_count = 1;
}
// Update innerHTML
writer();
// Call next iteration
main();
}, delay);
}
// Writes our innerHTML
const writer = (str, log) => {
if (log) {
console.log(str);
} else {
el.innerHTML = `Current count: ${current_count}`;
}
}
// Returns 1000 through 10000
const getDelay = () => {
return Math.ceil(Math.random() * 10) * 1000;
}
// Returns either 1 or 2
const getIteration = () => {
return Math.ceil(Math.random() * 2);
}
// Our main function to loop
const main = () => {
const delay = getDelay();
writer(`Next delay is ${delay}ms`, true);
timer(delay);
}
// Set Initial Value
writer();
// Main
main();
}
Hope this helps! If you have any questions, feel free to ask!

Page freezes when 3 second pause is inside a loop

When i loop through an array in javascript i have it pause for 3 seconds after each item. It does this successfully, but it freezes the webpage until the array completes.
function launchTutorial() {
HideFloatingMenu(); //freezes on page and it doesn't when i comment out the subsequent array loop
//highlightElement("diLeftColumn");
//the classes of each element to highlight in the tutorial
var tutorialClasses = [
"diLeftColumn",
"diMiddleColumn",
"diRightColumn"
];
var threeSec = new Date().getTime() + 3000;
for (var i = 0; i < tutorialClasses.length; i++) {
//$.each(tutorialClasses, function (key, value) {
if (i != 0) {
var now = new Date().getTime();
if (now >= threeSec) {
highlightElement(tutorialClasses[i]);
threeSec = new Date().getTime() + 3000;
}
else {
i = i - 1; //go back to this item if it hasn't been 3 seconds
}
}
else {
highlightElement(tutorialClasses[i]);
threeSec = new Date().getTime() + 3000;
}
}
}
I have tried setTimeout(), setInterval(0, delay(), 2 different custom sleep functions, and a while loop. none of them worked.
Use this. In javascript, when you do a while loop that takes time x, the whole page freezes for that time x. So using a while loop is no option. But you can use the function setTimeout like this. This will run the printNextElement function every 10 seconds (in my example).
At the console.log place, do your logic. And change the 10000 to your time.
const ar = ['Hello', 'World'];
let index = 0;
const printNextElement = () => {
console.log(ar[index]);
index += 1;
if(ar.length === index) {
return;
}
window.setTimeout(printNextElement, 10000);
};
printNextElement();

How do I delay this code running in JavaScript?

I have written this code to change an image:
change = function(){
for (r=0; r<6; r++){
for (i = 0; i < 6 ; i++) {
setInterval(imgfile(number=i+1), 5000);
}
}
}
imgfile= function(number){
a = 'document.getElementById("imgdiv").src = "images/'+number+'.svg"';
eval(a);
}
The function change() is called when a button is clicked.
When I press the button the image changes straight to 6.svg, when I want it to go through the images 1, 2, 3, 4, 5, 6 and to repeat it 6 times. When I change setInterval to change.setInterval or imgfile.setInterval it doesn't work at all. How do I fix this?
change = function(i=0){
imgfile(i%6+1);//change image
if(i<36) setTimeout(change,5000,i+1);//next image in 5 seconds
}
imgfile= function(number){
document.getElementById("imgdiv").src = "images/"+number+".svg";//no need to use ev(i||a)l
}
Instead of loop/interval mess you can simply start a timeout that restarts itself after changing the image... This code will loop over 6 images with a delay of 5 seconds and that 6 times...
Something like this, perhaps?
var index, imgCount, loopCount, imgTag, countdown;
index = 0;
imgCount = 6;
loopCount = 6;
imgTag = document.getElementById('imgdiv');
countdown = function () {
if (index < imgCount * loopCount) {
imgTag.src = 'images/' + index % imgCount + '.svg';
index = index + 1;
setTimeout(countdown, 5000);
}
};
countdown();
Here we're avoiding the double loop and using modular math (index % imgCount) to get the right file number.
For another question I wrote a nice utility function that has quite a number of uses, but can also handle this scenario very easily. The main issue is that there is no time elapsing between the different delays being set. So you are setting 6 different actions to all happen within 5000ms, and all will occur at the same moment.
Here's my original answer
Here's the utility function for that answer, along with its application to your problem.
function doHeavyTask(params) {
var totalMillisAllotted = params.totalMillisAllotted;
var totalTasks = params.totalTasks;
var tasksPerTick = params.tasksPerTick;
var tasksCompleted = 0;
var totalTicks = Math.ceil(totalTasks / tasksPerTick);
var initialDelay = params.initialDelay;
var interval = null;
if (totalTicks === 0) return;
var doTick = function() {
var totalByEndOfTick = Math.min(tasksCompleted + tasksPerTick, totalTasks);
do {
params.task(tasksCompleted++);
} while(tasksCompleted < totalByEndOfTick);
if (tasksCompleted >= totalTasks) clearInterval(interval);
};
// Tick once immediately, and then as many times as needed using setInterval
if (!initialDelay) doTick();
if (tasksCompleted < totalTicks) interval = setInterval(doTick, totalMillisAllotted / totalTicks);
}
// Do 6 actions over the course of 5000 x 6 milliseconds
doHeavyTask({
totalMillisAllotted: 5000 * 6,
totalTasks: 6,
tasksPerTick: 1,
initialDelay: false, // Controls if the 1st tick should occur immediately
task: function(n) { console.log('Set image to "images/' + (n + 1) + '.svg"'); }
});
You want to do setTimeout().
setTimeout pauses for the millesecond value and then does the code. Where setInterval runs the code every whatever milleseconds.
Yeah, don't do change.setInterval or whatever, it is just setInterval.
An example for you would be this inside the for loop to replace the setInterval function.
setTimeout(imgfile(i+1), 5000);

Changing speed of console.log in loop

I want to slow down console.log in my loop
// function update to actualize value
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
}
For example, show one console.log immediatly, and then, each .5s
How can we do that ?
(maybe with setTimeout() but I don't want start delay)
The simplest way would be to introduce a timeout before running the update again each time...
// function update to actualize value
function update() {
analyser.getByteFrequencyData(data);
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
requestAnimationFrame(function() {
setTimeout(update, 5000);
});
}
I used setTimeout() in preference over setInterval() as doing it this way (as well as moving the call to the end of the function) will make sure everything is completed, before starting the 5 second pause. It ensures there's no overlap, should the preceeding code take longer than 5 seconds.
First create a variable to store the time of the last console.log. Next, update that variable each time you console.log a value. Finally, add a check for the threshold.
var lastOutput = 0; // Setting this to 0 initially will ensure it runs immediately
var outputThreshold = 500; // in milliseconds
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
if (new Date().valueOf() - lastOutput > outputThreshold) {
// threshold met, output and update
var count=0;
for (var i=data.length; i--;) {
count+=data[i];
if(count >= 1) {
console.log(data);
}
};
lastOutput = new Date().valueOf();
}
}
update(); // fire first call to update, after that requestAnimationFrame() will handle future calls
If you want the time delay inside the for loop, you'd do this:
function update() {
requestAnimationFrame(update);
analyser.getByteFrequencyData(data);
var i = data.length - 1, count = 0;
function logger() {
count += data[i];
if (count >= 1)
console.log(data);
if (i-- >= 0)
setTimeout(logger, 500);
}
logger();
}
Now, things are going to be pretty messy because you're also using requestAnimationFrame() to schedule another iteration of the whole thing; that really won't make sense anymore. You'll probably want to have that wait until the logging process is done:
function update() {
analyser.getByteFrequencyData(data);
var i = data.length - 1, count = 0;
function logger() {
count += data[i];
if (count >= 1)
console.log(data);
if (i-- >= 0)
setTimeout(logger, 500);
else
requestAnimationFrame(update);
}
logger();
}

How to create pause or delay in FOR loop?

I am working on a website, where I need to create a pause or delay.
So please tell me How to create pause or delay in for loop in javascript or jQuery
This is a test example
var s = document.getElementById("div1");
for (i = 0; i < 10; i++) {
s.innerHTML = s.innerHTML + i.toString();
//create a pause of 2 seconds.
}
You can't use a delay in the function, because then the change that you do to the element would not show up until you exit the function.
Use the setTimeout to run pieces of code at a later time:
var s = document.getElementById("div1");
for (i = 0; i < 10; i++) {
// create a closure to preserve the value of "i"
(function(i){
window.setTimeout(function(){
s.innerHTML = s.innerHTML + i.toString();
}, i * 2000);
}(i));
}
var wonderfulFunction = function(i) {
var s = document.getElementById("div1"); //you could pass this element as a parameter as well
i = i || 0;
if(i < 10) {
s.innerHTML = s.innerHTML + i.toString();
i++;
//create a pause of 2 seconds.
setTimeout(function() { wonderfulFunction(i) }, 2000);
}
}
//first call
wonderfulFunction(); //or wonderfulFunction(0);
You can't pause javascript code, the whole language is made to work with events, the solution I provided let's you execute the function with some delay, but the execution never stops.
I tried all one, but I think this code is better one, it is very simple code.
var s = document.getElementById("div1");
var i = 0;
setInterval(function () {s.innerHTML = s.innerHTML + i.toString(); i++;}, 2000);
if you want to create pause or delay in FOR loop,the only real method is
while (true) {
if( new Date()-startTime >= 2000) {
break;
}
}
the startTime is the time before you run the while
but this method will cause the browsers become very slow
It is impossible to directly pause a Javascript function within a for loop then later resume at that point.
This is how you should do it
var i = 0;
setTimeout(function() {
s.innerHTML = s.innerHTML + i.toString();
i++;
},2000);
The following code is an example of pseudo-multithreading that you can do in JS, it's roughly an example of how you can delay each iteration of a loop:
var counter = 0;
// A single iteration of your loop
// log the current value of counter as an example
// then wait before doing the next iteration
function printCounter() {
console.log(counter);
counter++;
if (counter < 10)
setTimeout(printCounter, 1000);
}
// Start the loop
printCounter();
While several of the other answers would work, I find the code to be less elegant. The Frame.js library was designed to solve this problem exactly. Using Frame you could do it like this:
var s = document.getElementById("div1");
for (i = 0; i < 10; i++) {
Frame(2000, function(callback){ // each iteration would pause by 2 secs
s.innerHTML = s.innerHTML + i.toString();
callback();
});
}
Frame.start();
In this case, it is nearly the same as the examples that use setTimeout, but Frame offers a lot of advantages, especially if the you are trying to do multiple or nested timeouts, or have a larger JS application that the timeouts need to work within.
I am executing a function where I need access to the outside object properties. So, the closure in Guffa solution doesn't work for me. I found a variation of nicosantangelo solution by simply wrapping the setTimeout in an if statement so it doesn't run forever.
var i = 0;
function test(){
rootObj.arrayOfObj[i].someFunction();
i++;
if( i < rootObj.arrayOfObj.length ){
setTimeout(test, 50 ); //50ms delay
}
}
test();
The way I found was to simply use setInterval() to loop instead. Here's my code example :
var i = 0;
var inte = setInterval(() => {
doSomething();
if (i == 9) clearInterval(inte);
i++;
}, 1000);
function doSomething() {
console.log(i);
};
This loops from 0 to 9 waiting 1 second in between each iteration.
Output :
0 1 2 3 4 5 6 7 8 9
It is not possible to pause a loop. However you can delay the execution of code fragments with the setTimeout() function. It would not make a lot of sense to pause the entire execution anyway.
I am using while loop and check the pause variable to check the user pause/resume the code.
var pause = false;
(async () => {
for (let index = 0; index < 1000; index++) {
while (pause) {
await new Promise((res) => setTimeout(res, 1000));
console.log("waiting");
}
await new Promise((res) => setTimeout(res, 1000));
console.log(index);
}
})();
const pausefunc = async () => {
pause = true;
};
const playfunc = () => {
pause = false;
};
<button onclick="playfunc()">Play</button>
<button onclick="pausefunc()">Pause</button>
I used a do...while loop to put a delay in my code for a modal dialog that was closing too quickly.
your stuff....
var tNow = Date.now();
var dateDiff = 0;
do {
dateDiff = Date.now() - tNow;
} while (dateDiff < 1000); //milliseconds - 2000 = 2 seconds
your stuff....

Categories

Resources