7

I'm currently wondering if there is a better solution than passing this scope to the lambda-function via the parameter 'e' and then passing it to 'funkyFunction' using call()-method

setInterval(function(e){e.funkyFunction.call(e)}, speed, this)

(Minor question aside: I'd been reading something about memory-leaks in javascript. How does the lambda-function affect my memory? Is it better to define it first like var i = function(e)... and then passing it as a parameter to setInterval?)

6 Answers 6

12

My situation may have been a bit different, but here's what I did:

var self = this;
setInterval(function() { self.func() }, 50);

My scenario was that my code was inside a class method and I needed to keep correct scope as I didn't want the 'this' binding to resolve to the current window.

eg. I wanted to run MyClass.animate from MyClass.init using setInterval so I put this scope-keep code into MyClass.init

10

You can use native bind function.

function Loop() {
    this.name = 'some name for test';
    setInterval( (function(){//wrap the function as object
        //after bind, "this" is loop refference
        console.log(this);
    }).bind(this), 1000 );// bind the object to this (this is Loop refference)
}

var loop = new Loop();

paste this example in the console to see the result

0
5

What's wrong with simply relying on the outer-scope defined variable?

(function() { 

    var x = {};
    setInterval(function() {
       funkyFunction.call(x)
    }, speed);

})();
4
  • 1
    My Problem is, that setInterval is called out of another method of (in your example) 'x'. So I'm basically calling one method out of the other and tried find a tricky solution around closures, conserving a hugh object. Twisted thoughts? Commented Aug 15, 2010 at 18:25
  • 1
    @PenthousePauper: Having a reference to the object in a closure won't cause any memory issues. Passing this to setInterval would keep the object alive anyway. It's also Firefox-specific, so it won't work everywhere. Commented Aug 15, 2010 at 21:18
  • @MatthewCrumley How is it FF specific?
    – egerardus
    Commented Mar 19, 2013 at 21:57
  • @Geronimo This answer is fine; the FF-specific part is passing a parameter to the callback function, like in the question. Commented Mar 20, 2013 at 3:37
1

I had the same question, but there seems to be no built in solution, so here is a quick workaround I punched together:

function setScopedInterval(func, millis, scope) {
    return setInterval(function () {
        func.apply(scope);
    }, millis);
}

usage:

function MyClass() {
    this.timer = null;
    this.myFunc = function() { console.log('do some stuff'); };
    this.run = function() {
        this.timer = setScopedInterval(function () { this.myFunc(); }, 1000, this);
    };
    this.stop = function() { clearInterval(this.timer); };
}
var instance = new MyClass();
instance.run(); // will log to console every second
// until this line is called
instance.stop();

This only covers the use-case where you pass an actual function, not a string of code to be executed.

As for your question about memory leaks when using this functionality: it is not so much the problem with using setInterval as it is with anonymous functions in itself. If you use a reference to an object inside a lambda, this reference will keep the referenced object in memory for as long as the anonymous function exists. I think the function is destroyed with a call to clearInterval.

I don't think there is any benefit from assigning the function to a variable first, on the contrary, it will create another variable containing a reference that will not be garbage collected as long as the anon func exists...

4
  • Sorry about late comment but I fail to see an advantage of using setScopedInterval(function () {this.myFunc();}, 1000, this) over setInterval(function (x) {x.myFunc();}, 1000, this). How about var setScopedInterval = (f, time, scope, ...p) => setInterval((x, p) => x.f(...p), time, scope, p); instead? Example use: setScopedInterval(this.myFunc, 1000, this, p1, p2, p3).
    – 7vujy0f0hy
    Commented Jul 30, 2018 at 4:45
  • in 2013, when this answer was posted, arrow function were not yet implemented in javascript, that's the entire issue of the original question. The difference between anonymous functions and arrow functions is scope inheritance.
    – NDM
    Commented Aug 2, 2018 at 13:53
  • I was afraid that my modern notation would distract you from the proper issue... and it did. But forget it, there are novice-level errors in my code: x.f(...p) instead of x[f](...p) instead of f.apply(x, p). Likewise: x.myFunc() instead of x[myFunc]() instead of myFunc.apply(x). Fixing them turns my solutions into your solutions and essentially answers my own question.
    – 7vujy0f0hy
    Commented Aug 3, 2018 at 7:25
  • There are still other questions: 1. Why setScopedInterval(function () { this.myFunc(); }, 1000, this); rather than setScopedInterval(this.myFunc, 1000, this);? 2. Why not native setInterval(this.myFunc.bind(this), 1000); instead? Was there no bind method in 2013? 3. Why not grab this opportunity and allow setScopedInterval also pass arguments? Back in 2013, native setInterval didn’t allow passing arguments to the interval function and your function could implement it with minimal code. Like this: func.apply(scope, arguments).
    – 7vujy0f0hy
    Commented Aug 3, 2018 at 7:48
1

You may also have a look at the YUI Framework. It's fine for building applications and easy to learn.

YUI2: YAHOO.lang.later(when, scope, fn, args, periodic);

YUI3: Y.later(when, scope, fn, args, periodic);

UPDATE as example

Using YUI and jQuery (Do not forget enable $.noConflict())

var jQuerySelector = jQuery("div[class^='form-field-']");

jQuerySelector.hide();
jQuery(jQuerySelector[0]).show();


YAHOO.lang.later(5000, jQuery, function(jQuerySelector) {
    if((!(this.index)) || (this.index == (jQuerySelector.length))) {
        this.index = 0;
    }

    jQuerySelector.hide();

    this(jQuerySelector[this.index++]).show();
}, jQuerySelector, true);

In short

  • 1º parameter: 5000 on every 5000 miliseconds, 3º parameter (a function) will be executed
  • 2º parameter: jQuery Object in which will be referenced by using this
  • 3º parameter: function which will be executed. It receives as parameter either an array or an object passed as 4º parameter
  • 5º parameter: true if true, executes continuously at supplied interval until canceled

see http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_later

UPDATE No need for $.noConflict() because YUI does not use $ in any way.

0

There are two important distinctions to make.

1) Do you want a reference to the passed parameter so that the timeout function can track changes made to it, or do you want a clone of the passed parameter?

2) Do you want to be able to capture a reference to the timeout in case you want to cancel it? (Yes!)

// Normal setTimeout: retains a reference to `test` and returns the bad value
var test = 'test: good';
var timer = setTimeout(function() { console.log(test); }, 1000);
test = 'test: bad';

// Test2 receives a clone of `test2` and returns the good value, but does so right away, not on a timeout
var test2 = 'test2: good';
var timer2 = setTimeout((function() { console.log(test2); })(test2), 1000);
test2 = 'test2: bad';

// Test3 receives a clone of `test3` and returns the good value, but doesn't return a reference to the timeout, and can't be canceled
var test3 = 'test3: good';
var timer3 = function(test3) { setTimeout(function() { console.log(test3); }, 1000); }(test3);
test3 = 'test3: bad';

// Test4 receives a clone of `test4` and returns the good value, and correctly returns timeout reference
var test4 = 'test4: good';
var timer4 = function(test4) { return setTimeout(function() { console.log(test4); }, 1000); }(test4);
test4 = 'test4: bad';

// Test5 retains a reference to `test5` and returns the bad value
var test5 = 'test5: good';
var timer5 = setTimeout((function() { console.log(test5); }).bind(test5), 1000);
test5 = 'test5: bad';

// Did we capture references to the timeouts?
console.log(typeof timer);
console.log(typeof timer2);
console.log(typeof timer3);
console.log(typeof timer4);
console.log(typeof timer5);

Not the answer you're looking for? Browse other questions tagged or ask your own question.