Calling function from array using setTimeout in Javascript - javascript

I am creating two function name call and call2 and putting this two functions in array x. In another function called timer i am trying to call this function every 2 seconds. But its giving error expected an assigment or function call instead show and expression.
And also i dont want this functions to run when i create an array.
this my code http://jsbin.com/IMiVadE/2/
function call(name)
{
console.log("Hello " + name);
}
function call2()
{
console.log("Hello world");
}
var x = [call("Nakib"), call2()];
var i = 0;
function timer(x, i)
{
x[i];
i++;
if(i<x.length)
window.setTimeout(timer(x, i), 2000);
}
timer(x, i);

You have some mistakes in your code:
call2() calls the function. Correct: call2 is the reference to the function.
x[i] accesses the value (the function reference). You need x[i]() to execute the function.
Here is your code working: http://jsbin.com/IMiVadE/6/edit
sayHello(name) is a function that generates new functions that output a specific "Hello ..." text.
function sayHello(name)
{
return function () {
console.log("Hello " + name);
}
}
function timer(x, i)
{
x[i]();
if (i < x.length - 1) {
setTimeout(function () {
timer(x, i + 1);
}, 2000);
}
}
var x = [sayHello("Nakib"), sayHello("world")];
setTimeout(function () {
timer(x, 0);
}, 2000);

setTimeout needs a function without parameter. So you can wrap the recursive call to timer in an anonymous function to fix that:
window.setTimeout(function(){timer(x, i)}, 2000);
Also, the first line in your timer function, consisting only of x[i];, is useless, although it probably isn't the cause of your problem.

The setTimeout function takes a function as a parameter, you are executing the timer function before it is passed and since timer doesn't return anything, undefined is being passed to the timeout;
window.setTimeout(timer(x, i), 2000); // is the same as...
window.setTimeout(undefined, 2000);
It should be;
window.setTimeout(function() { timer(x, i) }, 2000); // or...
window.setTimeout(timer.bind(this, x, i), 2000);
I'm not sure if this is intentional, but you are doing the same thing with your array;
var x = [call("Nakib"), call2()];
This will execute the functions and their results will be stored in the array. Is this what you want?

Related

Variable scope in setInterval [duplicate]

I have a function:
setInterval(function () {
var counter = 0;
(function() {
counter = counter + 1;
console.log(counter);
})(counter)
}, 1000)
Why does not it increment the counter? (instead, it logs 1's). How to make it log ascending numbers? (1, 2, 3, ....)
You could use a function which returns a function as closure over counter.
setInterval(function(counter) {
return function() {
console.log(++counter);
};
}(0), 1000);
You are passing an argument to your anonymous function, but you aren't assigning that argument to a variable. You forgot to put the arguments in the function definition.
You are creating new variables with every iteration instead of sharing the variable between them. You need to turn your logic inside out.
(function(closed_over_counter) {
setInterval(function() {
closed_over_counter++;
console.log(closed_over_counter);
}, 1000);
})(0);
Since you are using an IIFE instead of a function you can call multiple times, this is pretty pointless. You might as well just declare the variable inside the closure.
(function() {
var counter = 0;
setInterval(function() {
counter++;
console.log(counter);
}, 1000);
})();
Obscured version of Nina Scholz's answer with arrow functions:
setInterval(((counter) => () => console.log(++counter))(0), 1000);

Break a series of timeout commands?

If I have a function like so:
function x()
{
animate(a, 2000);
animate(b, 3000);
animate(c, 4000);
}
Where - a, b & c - are variables representing elements on the page, and the number is a parameter passed to an animate() function that uses it as a duration value for a timeout, like so:
function animate(src, dur)
{
setTimeout(function() {
src.style.opacity = 1;
}, dur);
}
Everything so far is fine, but if I want the ability to break out of the animation loop, how do I go about it? Will clearTimeout() be what I'm looking for?
Variables that have been assigned a timeout, may be passed to the clearTimeout function, which will cease the function. You can store these variables in an array and easily clear all timeouts by iterating this array and passing the timeout to the clearTimeout function.
var timeouts = [];
/* Save timeouts into a variable and push to an array */
function animate(src, dur)
{
var timeout = setTimeout(function() {
src.style.opacity = 1;
}, dur);
timeouts.push(timeout);
}
/** Clear all timeouts**/
function clearTimeouts(){
for(var i = 0; i < timeouts.length; i++){
clearTimeout(timeouts[i]);
}
}
//Create timeouts
x();
//Invoke clearTimeouts
clearTimeouts();
Yes, clearTimeout() is the right way to go:
function animate(src, dur)
{
return setTimeout(function() {
src.style.opacity = 1;
}, dur);
}
And save returned identifier:
function x()
{
var aId = animate(a, 2000);
var bId = animate(b, 3000);
var cId = animate(c, 4000);
}
Later you simply call clearTimeout(aId) or whichever you desire. BTW there is no loop in your code, setTimeout() executes only once, as opoosed to setInterval().

Why do I need to have anonymous function in the setTimeout for this code to work?

I'm reading a tutorial for Nodejs, but I can't understand this snippet of code, please explain it to me.
function async(arg, callback) {
console.log('do something with \''+arg+'\', return 1 sec later');
setTimeout(function() { callback(arg * 2); }, 1000);
}
function final() { console.log('Done', results); }
var items = [ 1, 2, 3, 4, 5, 6 ];
var results = [];
var running = 0;
var limit = 2;
function launcher() {
while(running < limit && items.length > 0) {
var item = items.shift();
async(item, function(result) {
results.push(result);
running--;
if(items.length > 0) {
launcher();
} else if(running == 0) {
final();
}
});
running++;
}
}
launcher();
This code produces run 2x then pause for a second then run 2x again until there is no item in the items array.
But when I removed the anonymous function in the setTimeout:
setTimeout(callback(arg*2), 1000);
Then the code runs with out stopping any second. Why?
Then the code runs with out stopping any second. Why?
Because instead of passing a function to setTimeout, you are passing the return value of a function call to it.
The function call executes immediately, and then setTimeout does nothing with the return value because it (presumably) isn't a string or a function.
Don't remove the anonymous function wrapper.
The reason the anonymous delegate is needed is because setTimeout expects an object of type function as it's first argument.
So you can either give it just a pre-defined function name directly:
function foo()
{
//do something
}
setTimeout(foo, 1000);
Or a delegate:
setTimeout(function(){/*dosomething*/}, 1000);
But this:
setTimeout(foo(param), 1000);
Is invalid because foo(param) isn't semantically correct in this context.
The alternative is to do something like:
setTimeout("foo(param);", 1000);
Because setTimeout will also accept a string of a function, but this is a bad idea because you lose the current scope and it's a pain to debug.

Passing callback as a parameter to function

I trying to write a generic function which takes the function and number of milliseconds as argument and it sets it to setTimeout function. I tried: like
$("#Delay").click(function() {
delayCallBack(someFunction(a, b), 100);
});
In delayCallBack function:
function delayCallBack(event, time) {
setTimeout(function() {
event();
}, time);
};
But this is not working and throwing me JavaScript error. Can someone please help me with the right way of doing this?
Replace
$("#Delay").click(function() {delayCallBack(someFunction(a,b), 100);});
With
$("#Delay").click(function() {delayCallBack(function(){someFunction(a,b)}, 100);});
The first line executes someFunction(a,b) instead of what the second line does, passing a reference to a function to execute.
() invokes a function, so you call the function rather than pass it. Pass the function like normal variable:
$("#Delay").click(function() {
delayCallBack(someFunction, 100);
});
Your function can be cleaned up a little... then again it's not that different from just calling setTimeout directly..
function delayCallBack(callback, time) {
setTimeout(callback, time);
}
To pass the arguments a and b, you can create a new function out of your existing function that partially applies a and b, then pass it:
$("#Delay").click(function() {
delayCallBack(someFunction.bind(null, a, b), 100);
});
More of .bind. Demo: http://jsfiddle.net/Pd5JZ/2/
This question has a surprisingly tricky answer in JavaScript, since you pass in the values a and b. Consider Gerard Sexton's solution
$("#Delay").click(function() {
delayCallBack(function() {
someFunction(a,b);
}, 100);
});
What that code does depends on where a and b are defined and what happens to them after the handler is bound. Suppose we put it in context:
$(function () {
var a, b;
a = b = 5;
$("#Delay").click(function() {
delayCallBack(function() {
someFunction(a,b);
}, 100);
});
a = b = 7;
}
Then someFunction will be called with a=b=7, not a=b=5. If you want the first values, you have to copy the values when you bind the handler. Like so:
$(function () {
var a, b;
function bind(a, b) {
$("#Delay").click(function() {
delayCallBack(function() {
someFunction(a,b);
}, 100);
});
}
a = b = 5;
bind(a, b);
a = b = 7;
}
I was also getting the same error : setTimeout call (missing quotes around argument?).
Gerard Sexton explanation is right.
The correct code should be
$("#Delay").click(function() {delayCallBack(function(){someFunction(a,b)}, 100);});

How do I find out how many times a function is called with javascript/jquery?

Perhaps an odd question but here it goes: I have a function which I call periodically and within that function I need to know which iteration I'm in, or how many times the function has been called. A simplified version of the problem:
jQuery( document ).ready( function(){
setInterval( "myFunction()", 3000 );
});
function myFunction()
{
alert( "I have been called X times" );
}
So, how do I figure out the X in the above code?
Easy version: make a global variable like in codeling's answer. The problem - if some other code also defines a global variable with the same name, you're both in trouble.
Easy extended version - give the variable a crazy name that nobody will ever use: calledTimesED7E69A7B141457CA8908A612E3D7A3A
Clever version: append that variable to an existing global variable. Remember - everything's an object in Javascript!
$(function(){ setInterval(myFunction, 3000); });
function myFunction()
{
myFunction.calledTimes++;
alert( "I have been called " + myFunction.calledTimes + " times" );
}
myFunction.calledTimes = 0;
Traditional version: use scoping to hide that variable.
$(function()
{
var calledTimes = 0;
setInterval(function()
{
calledTimes++;
alert( "I have been called " + calledTimes + " times" );
}, 3000);
});
This hides "myFunction" though, so let's try again with a tricky kind of scoping:
var myFunction = null;
(function()
{
var calledTimes = 0;
myFunction = function()
{
calledTimes++;
alert( "I have been called " + calledTimes + " times" );
}
})();
$(function () { setInterval(myFunction, 3000); });
... and there are a zillion other ways you would hide that variable with scoping. Just pick your favorite.
You could simply use a global variable, which is increased each time you call the function:
var myFuncCalls = 0;
function myFunction()
{
myFuncCalls++;
alert( "I have been called " + myFuncCalls + " times" );
}
As soon as your code gets a little more complex (or if you use a lot of other libraries), you should, however, consider using scoping as shown in the other answers here (best explained in the one by Vilx).
Here's another interesting solution that doesn't use an external variable. The best part about this is you can leave any pre-existing functions untouched and call them as you would normally. That means if you're attempting to "tap in" to an function in an existing library, this will work very well for you. It adds an unobtrusive counter and allows you to continue calling existing functions normally; even with arguments!
// no js library required
// pre-existing function
var a = function(){
console.log("pre-existing function function");
console.log("arguments:", arguments);
};
// add counter func
var addFnCounter = function(target){
var swap = target;
var count = 0;
return function(){
swap.apply(null, arguments);
count++;
console.log("func has been called " + count + " times");
console.log("\n");
};
};
// usage
a = addFnCounter(a);
// call a() as you would normally
a();
a(1,2,3);
a('hello', 'world');
// using your setInterval example
setInterval(a, 3000);
Output
pre-existing function function
arguments: []
func has been called 1 times
pre-existing function function
arguments: [1, 2, 3]
func has been called 2 times
pre-existing function function
arguments: ["hello", "world"]
func has been called 3 times
setInterval output
pre-existing function function
arguments: []
func has been called 4 times
pre-existing function function
arguments: []
func has been called 5 times
pre-existing function function
arguments: []
func has been called 6 times
See it working here on jsfiddle
You'll have to use a closure.
Normally you would use a static variable. in Javascript it would look something like:
jQuery( document ).ready( function(){
setInterval( myFunction, 3000 );
});
var myFunction = (function(){
var count = 0;
return function(){
count++
alert( "I have been called " + count + " times");
}
})();
Demonstration: http://jsfiddle.net/MZQ83/2/
A static variable is cleaner and it won't pollute your outer scope either, compared to a closure or a decorator as in other answers.
var foo = function(){
alert( ++foo.count || (foo.count = 1) );
}
// test
function callTwice(f){ f(); f(); }
callTwice(foo) // will alert 1 then 2
or
callTwice( function bar(){
alert( ++bar.count || (bar.count = 1) );
}); // will alert 1 then 2
the second one is a named anonymous function. And note this syntax:
var foo = function bar(){ /* foo === bar in here */ }
Create a global variable and initialize by zero. then increment by one when myfunction() called. Display that variable instead of X.
ES6 / ES2015
You can use a Proxy for your function utilising the apply() trap:
const addCounter = fn => {
let count = 0; // keep count
//define handler
const handler = {
apply() {
//do something with this counter
console.log(`I have been called ${++count} times `);
return Reflect.apply(...arguments); //call the function normally
}
}
//wrap the function into a proxy that uses the handler and return it
return new Proxy(fn, handler);
}
setInterval( addCounter(myFunction), 1000 );
function myFunction() { //sample operation - move an image
const img = document.querySelector("img");
let offset = img.offsetLeft + 10;
if (offset > 100) //return to start
offset = 0;
img.style.left = `${offset}px`;
}
img {
position: absolute;
}
.as-console-wrapper {
max-height: 45px !important;
}
<img src="https://picsum.photos/150" />
You can use an Immediately Invoking Function Expression (or IIFE) to create a closure around the counter function. You can do it like this with ES6 Arrow functions:
const counterFunction = (() => {
let counter = 0;
return () => console.log(++counter);
})();
counterFunction();
counterFunction();
counterFunction();
Or with normal function expressions:
var counterFunction = (function() {
var counter = 0;
return function() {
console.log(++counter);
};
})();
counterFunction();
counterFunction();
counterFunction();
Read more about IIFEs
Read more about closures
There is an inbuilt function in JS called console.count()
My approach would add a property “count” to the function itself.
Just add one line at the beginning of your function you want to have tracked calls.
function func() {
func.count = (func.count || 0) + 1;
// first time you call the function func.count is undefined so use 0 instead
console.log("hi");
}
func();
console.log(func.count) // 1
func();
func();
func();
console.log(func.count) // 4
Functions are objects in javascript after all. No pollution of global namespace, no wrapping or closures needed, very simple to understand and to write.

Categories

Resources