How to kick-ass pass scope through "setInterval"

16,339

Solution 1

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

(function() { 

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

})();

Solution 2

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

Solution 3

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

Solution 4

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.

Solution 5

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...

Share:
16,339

Related videos on Youtube

PenthousePauper
Author by

PenthousePauper

Updated on April 30, 2022

Comments

  • PenthousePauper
    PenthousePauper almost 2 years

    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?)

  • PenthousePauper
    PenthousePauper over 13 years
    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?
  • Matthew Crumley
    Matthew Crumley over 13 years
    @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.
  • egerardus
    egerardus about 11 years
    @MatthewCrumley How is it FF specific?
  • Matthew Crumley
    Matthew Crumley about 11 years
    @Geronimo This answer is fine; the FF-specific part is passing a parameter to the callback function, like in the question.
  • 7vujy0f0hy
    7vujy0f0hy over 5 years
    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).
  • NDM
    NDM over 5 years
    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.
  • 7vujy0f0hy
    7vujy0f0hy over 5 years
    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
    7vujy0f0hy over 5 years
    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).