recursive method in Vue JS - javascript

I am attempting to call a method within itself in Vue, however I'm getting the following error
this.loop is not a function. (In 'this.loop()', 'this.stagger'
is undefined)
here is the method:
loop: function () {
var n = $(".item").length;
var i = 1;
var m = n + 5;
setTimeout( function () {
$('.item:nth-child('+i+')').addClass('show');
var x = i - 2;
var y = x - 2;
i = i + 3;
// for 2 columns:
// x = i - 1;
// i = i + 2;
$('.item:nth-child('+x+')').addClass('show');
$('.item:nth-child('+y+')').addClass('show'); // remove for 2 columns
if (i < m) {
this.loop() // error occurs here
}
}, 100)
}

This is because this no longer refers to the object when in the callback function of setTimeout. There are several solutions.
You could change the function to an arrow function:
setTimeout( () => {
That way this will retain its original value, also within the callback.
Or, you could bind this to the function:
setTimeout( function () {
// ...
}.bind(this), 100)
//^^^^
Or, you could copy thisand use that instead:
var that = this;
setTimeout( function () {
// ...
that.loop();
// ...
}, 100)
Avoiding reinitialisation
Currently your recursive calls will also reset the variables, including i.
Solve this, by passing i as argument:
loop: function (i = 1) { // <<---- default value
var n = $(".item").length;
var m = n + 5;
if (i >= m) return; // <<-----
setTimeout(() => {
$('.item:nth-child('+i+')').addClass('show');
var x = i - 2;
var y = x - 2;
$('.item:nth-child('+x+')').addClass('show');
$('.item:nth-child('+y+')').addClass('show');
this.loop(i+3); // <<------
}, 100);
}

Related

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

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.

Javascript, calling asynchronous function in a for loop [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I wish to call an asynchronous function in a for loop. I am having significant trouble doing so and am getting a variety of errors such as undefined variables and such.
Evaluator.prototype.asyncEval = function(predictor) {
let self = this;
let metric = 0; //METRICS SHOULD BE UPDATED BY ASYNC FUNCTION
for (let i = 1; i < this.fullTraces.length; i++) {
(function(index){
let deltaTime = self.fullTraces[i][2] - this.fullTraces[i-1][2];
let subTraces = self.fullTraces.slice(0, i);
predictor.predict(subTraces, (dist) => { // ASYNC FUNCTION
if (dist !== null) {
let result = dist.getTopK(1);
let pX = result[0][0][0];
let pY = result[0][0][1];
let x = self.fullTraces[i][0];
let y = self.fullTraces[i][1];
let a = pX - x;
let b = pY - y;
metric += Math.sqrt(a*a + b*b);
}
});
}(i));
}
metric /= this.fullTraces.length - 1;
return metric;
}
My asynchronous function predictor.predict() is actually using a POST request to get results from my web server.
YourPredictor.prototype.predict = function(trace, callback) {
return asyncPostRequest('https://0.0.0.0:5000/prediction', trace, responseText => {
prediction = JSON.parse(responseText);
let pred = [prediction['xs'], prediction['ys'], 'm'];
let dist = Dist.NaiveDistribution.from(pred, mouseToKey);
dist.set(pred, 1);
callback(dist);
});
}
How can I get this to work? I am running this on Chrome. I know there is the new await and async from ES7, but I don't want to use something that bleeding edge yet.
You need to refactor the code to replace the loop with a self-invoking loop, so that each time the asynchronousis called, the result from it is handed back, then iteration is checked, if i
Since the main code is asynchronous you will also need a callback for the initial call function (the doneCallback below).
Example
I left in the original code where it is expected to work, but made a couple of changes for it to work here.
function Evaluator() {}; // dummy for test
Evaluator.prototype.asyncEval = function(predictor, doneCallback) {
let self = this;
let metric = 0;
let length = 10; //this.fullTraces.length;
let i = 1;
// replaces for-loop
(function loop() {
self.predict(0, (dist) => {
// ...
metric += dist;
if (++i < length) loop();
else {
// ...
doneCallback(metric);
}
});
})();
}
// note: I changed prototype parent in this example
Evaluator.prototype.predict = function(trace, callback) {
//...
setTimeout(callback, 100, Math.random() * 100); // simulate async call
}
// TEST
var test = new Evaluator();
test.asyncEval(0, function(result) {
document.querySelector("div").innerHTML = result;
});
<div>Calcing...</div>
Example leaving the original code in place at the intended locations:
function Evaluator() {}; // dummy for test
Evaluator.prototype.asyncEval = function(predictor, doneCallback) {
let self = this;
let metric = 0; //METRICS SHOULD BE UPDATED BY ASYNC FUNCTION
let length = 10; //this.fullTraces.length;
let i = 1;
// replaces for-loop
(function loop() {
//let deltaTime = self.fullTraces[i][2] - this.fullTraces[i - 1][2];
//let subTraces = self.fullTraces.slice(0, i);
self.predict(0, (dist) => { // ASYNC FUNCTION
//predictor.predict(subTraces, (dist) => { // ASYNC FUNCTION
/*if (dist !== null) {
let result = dist.getTopK(1);
let pX = result[0][0][0];
let pY = result[0][0][1];
let x = self.fullTraces[i][0];
let y = self.fullTraces[i][1];
let a = pX - x;
let b = pY - y;
metric += Math.sqrt(a * a + b * b);
}*/
metric += dist;
if (++i < length) loop();
else {
//metric /= this.fullTraces.length - 1;
//return metric; <- don't use, instead use:
doneCallback(metric);
}
});
})();
}
// note: I changed prototype parent in this example
Evaluator.prototype.predict = function(trace, callback) {
setTimeout(callback, 100, Math.random() * 100); // simulate async call
/*return asyncPostRequest('https://0.0.0.0:5000/prediction', trace, responseText => {
prediction = JSON.parse(responseText);
let pred = [prediction['xs'], prediction['ys'], 'm'];
let dist = Dist.NaiveDistribution.from(pred, mouseToKey);
dist.set(pred, 1);
callback(dist);
});*/
}
// TEST
var test = new Evaluator();
test.asyncEval(0, function(result) {
document.querySelector("div").innerHTML = result;
});
<div>Calcing...</div>
If you dont want to use async + await combination, I would suggest to take a look at this post.
Asynchronous for cycle in JavaScript
I'm using this asyncLoop function and it's working great:
The function takes three arguments: 1) iterations, 2) a loop callback function and 3) Done callback function,
Check out the code:
function promise1(param){
return new Promise((resolve, reject) => setTimeout(() => { resolve(`Promise1 ${param} Done`)}, 2000))
}
function asyncLoop(iterations, func, callback) {
var index = 0;
var done = false;
var loop = {
next: function() {
if (done) {
return;
}
if (index < iterations) {
index++;
func(loop);
} else {
done = true;
callback();
}
},
iteration: function() {
return index - 1;
},
break: function() {
done = true;
callback();
}
};
loop.next();
return loop;
}
var asyncProc = ["Process1", "Process2", "Process3"]
asyncLoop
(
asyncProc.length,
(loop) => { promise1(asyncProc[loop.iteration()]).then((msg) =>{ console.log(msg); loop.next() }) },
() => { console.log("ALL DONE!")});
You cannot return the value of the "metric" synchronously if it is being modified asynchronously. You'll need to pass a callback into your method so the "metric" can be returned when it is ready.
Evaluator.prototype.asyncEval = function (predictor, callback) {
let self = this;
let metric = 0; //METRICS SHOULD BE UPDATED BY ASYNC FUNCTION
let callbacks = 0; // Keep a counter for the asynchronous callbacks
for (let i = 1; i < self.fullTraces.length; i++) {
let deltaTime = self.fullTraces[i][2] - this.fullTraces[i - 1][2];
let subTraces = self.fullTraces.slice(0, i);
// Queue up an asynchronous callback
predictor.predict(subTraces, (dist) => { // ASYNC FUNCTION
if (dist !== null) {
let result = dist.getTopK(1);
let pX = result[0][0][0];
let pY = result[0][0][1];
let x = self.fullTraces[i][0];
let y = self.fullTraces[i][1];
let a = pX - x;
let b = pY - y;
metric += Math.sqrt(a * a + b * b);
}
// Decrement the counter and check if we're done
if (--callbacks === 0) {
callback(metric / (self.fullTraces.length - 1));
}
});
// Increment the counter
callbacks++;
}
};

How does implicit passing of object reference work?

I have some Javascript code which works fine so far but I do not understand the how the variable "me" is set in the function "run"?
GameLoop.prototype.run = function() {
this.startTime = new Date().getTime();
var currentTimeMillis = this.startTime;
var loops;
var interpolation=0.0;
this.running=true;
return function(me){
loops = 0;
while (new Date().getTime() > currentTimeMillis && loops < me.MAX_FRAMESKIP) {
me.updateGame();
currentTimeMillis += me.SKIP_TICKS;
loops++;
}
interpolation = parseFloat(new Date().getTime() + me.SKIP_TICKS - currentTimeMillis) / parseFloat(me.SKIP_TICKS);
me.drawGame(interpolation);
}
}
The function is called continuously by the browser's animate function below. Since I do not pass any reference to the call f.run(), i guess the correct reference to me is set implicitly. Can someone explain me or give me some useful links which explains this behaviour?
GameLoop.prototype.recursiveAnim = function() {
var f = this.run();
f.run();
this.animFrame( this.recursiveAnim );
};
By calling run you get a function in return, that function has one parameter and its called me.
For example
var x = function () { return function (me) { return me; } }
// by calling x, you get the function: `function (me) { return me; }
var f = x();
console.log(f(1)); // answer is 1

How to use a variable number of arguments in a JavaScript function

I am having trouble understanding how to return information to the first function from the second when there are multiple arguments. Now I know the following code works.
function One() {
var newVal = 0;
newVal = Too(newVal);
console.log(newVal);
}
function Too(arg) {
++arg;
return arg;
}
But what if I try to complicate things by adding arguments and a setinterval.
function One() {
var newVal = 0;
var z = 3;
var y = 3;
var x = 1;
newVal = Too(newVal);
var StopAI2 = setInterval(function () {
Too(x, y, z, newVal)
}, 100);
}
function Too(Xarg, Yarg, Zarg, newValarg) {
Xarg*Xarg;
Yarg*Yarg;
Zarg*Zarg;
++newValarg;
return newValarg;
}
I'm not sure what to do with the newVal = line of code. I only want to return the newVal not x,y,z.
This is what I think you're trying to ask:
How can I operate on the 4th argument to a function when only one argument is passed?
The answer to that question is this:
If you want to operate on the 4th argument of a function, at least 4 arguments must be passed to the function.
There are a few ways you can approach your problem differently.
#1
If there's one argument that is always necessary, make sure it's the first argument:
function Too(mandatoryArg, optionalArg1, optionalArg2) {
alert(++mandatoryArg);
if (optionalArg1) {
alert(++optionalArg1);
}
}
#2
Pass placeholder values for all the undefined or unknown arguments.
You might use null, undefined, or ''.
alert(Too(null, null, 4));
function Too(optArg1, optArg2, mandatoryArg) {
alert(++mandatoryArg);
}
#3
Make a decision based on the number of arguments:
function Too(optArg1, optArg2, optArg3) {
var numArgs = arguments.length;
if (numArgs === 1) {
alert(++optArg1);
}
if (numArgs === 3) {
alert(++optArg3);
}
}
EDIT
"Will this update a variable in the first function?"
Let's use an actual example that demonstrates something:
function one() {
var a = 0;
var b = 25;
var c = 50;
var d = -1;
d = two(a, b, c);
alert("a: " + a);
alert("b: " + b);
alert("c: " + c);
alert("d: " + d);
}
function two(a, b, c) {
++a;
++b;
++c;
if (arguments.length === 1) {
return a;
}
if (arguments.length === 3) {
return c;
}
}
Invoking one() will cause the following alerts:
a: 0
b: 25
c: 50
d: 51
Only the value of d is modified in function one().
That's because d is assigned the return value of two().
The changes to a, b, and c, inside two() have no effect on the values of a, b, and c inside one().
This would be the case even if the arguments for two() were named a, b, and c.
Here's a fiddle with the code above.
EDIT #2
Here is one way you could create functions that move a game object:
var FORWARD = 0;
var BACK = 1;
var LEFT = 2;
var RIGHT = 3;
// use an object with three values to represent a position
var pos = {
x: 0,
y: 0,
z: 0
};
pos = moveObject(pos, FORWARD);
printPosition(pos);
pos = moveObject(pos, LEFT);
printPosition(pos);
pos = moveObject(pos, FORWARD);
printPosition(pos);
pos = moveObject(pos, LEFT);
printPosition(pos);
// invoking moveObject() with one argument
// will move the object forward
pos = moveObject(pos);
printPosition(pos);
function moveObject(position, direction) {
// assume FORWARD if no direction is specified
if (typeof direction === 'undefined') {
direction = FORWARD;
}
if (direction === FORWARD) {
++position.z;
}
if (direction === BACK) {
--position.z;
}
if (direction === LEFT) {
--position.x;
}
if (direction === RIGHT) {
++position.x;
}
return position;
}
function printPosition(pos) {
alert(pos.x + ", " + pos.y + ", " + pos.z);
}
Here's a fiddle that shows a working demo of another approach.
There are two concepts that are at play here.
1 . Variable number of function parameters (or optional parameters).
If you are going to call the same function with different number of parameters (this will eventually lead to a world of headache), you need to determine (inside the function) how this function was called. You can use arguments object available inside each function:
function Too() {
if (arguments.length == 4) {
arguments[0]*arguments[0];
arguments[1]*arguments[1];
arguments[2]*arguments[2];
return ++arguments[3];
} else if (arguments.length == 1) {
return ++arguments[0];
} else {
// you decide what to do here
}
}
2 . Asynchronous code execution.
Realize that Too which is called when interval expires, executes well after One completes and returns. If you want Too to affect newVal variable, and somehow get at this new value afterwards, - make newVal variable global.

How does JavaScript function call with prototype function name.argument work?

How does JavaScript function call with prototype function name.argument work in the following program?
function getLAdd() {
// this sets all the variables containing positions of ball and bar with their respective ids.
var ladd = 0;
var pball = $("#ball");
var pbar = $("#bar");
var bar_position = pbar.position();
var ball_position = pball.position();
if (ball_position.top >= window.innerHeight - 100) {
if (ball_position.left - 10 >= bar_position.left && ball_position.left - 10 <= bar_position.left + 100) {
ladd = -2;
}
if (ball_position.left + 10 <= bar_position.left + 200 && ball_position.left + 10 >= bar_position.left + 100) {
ladd = 2;
}
}
// how does getLAdd.ladd work ? Is this a type of dynamic call ?
if (ladd == 0) {
ladd = getLAdd.ladd;
}
if (ball_position.left <= 15 || ball_position.left >= window.innerWidth - 40)
ladd = -ladd;
getLAdd.ladd = ladd;
return ladd;
}
Functions in JavaScript are objects, so you can add properties to them.
In this code a property named ladd has been added to the getLAdd function, and is being retrieved on this line:
ladd = getLAdd.ladd;
and is being updated on this line:
getLAdd.ladd = ladd;
You can do the same thing with any function.
function f() {
// get the property
console.log(f.foo); // bar
}
// add a property to the function object
f.foo = "bar";
// get the property
console.log(f.foo); // bar
// call the function
f();

Categories

Resources