3

I'm trying to make a simple task queue with setInterval with a linked-list. I created a class with linkedlist and a setInterval function will keep calling a member function to consume the job.

function job_queue(){
    this.job = null;
    this.pointer = this.job;
    this.job_dispatcher = null; 
    this.length = 0;
}
job_queue.prototype.add_job = function( job ){
    if( this.job == null ){
        console.log('1st');
        this.job = {
            job:job,
            next:null
        };
        this.pointer = this.job;
        this.length = 1;
    }else{
        console.log('2nd');
        this.pointer.next = { 
            job:job,
            next:null
        };
        this.pointer = this.pointer.next;
        this.length++;
    }
};

job_queue.prototype.event_handler = function(){
        if( typeof this.job['job'] == 'undefined'){
            console.log('??');
        }
        if( this.job.job != null ){
            console.log('hi');
            this.job.job();
            this.job = this.job.next();
        }

}

job_queue.prototype.start_dispatch = function(){
    if( this.job_dispatcher == null ){
        console.log( this.event_handler );
        this.job_dispatcher = setInterval( this.event_handler,1000);
    }
}



var jq = new job_queue();
function a(){
    console.log('hi');
};
function b(){
    console.log('hi2');
}
jq.add_job(a);
jq.add_job(b);
jq.add_job(a);
jq.start_dispatch();

However, when the event_handler function gets called , the program crashes with the log

 if( typeof this.job['job'] == 'undefined'){

It seems like it can not access the member variable by calling member function with setInterval. I would like to ask what exactly happened with these lines of code and how can I achieve the goal?

Jian
  • 43
  • 6
  • See this answer for an explanation of how `this` works: http://stackoverflow.com/questions/13441307/how-does-the-this-keyword-in-javascript-act-within-an-object-literal/13441628#13441628 – slebetman Sep 04 '15 at 02:12

3 Answers3

2

As others have pointed out, setInterval() calls your function in a different context. This means that the value of this within the function passed to setInterval() (in this case, event_handler()) will not be pointing to the correct object.

A great solution to this problem is JavaScript's bind function:

this.job_dispatcher = setInterval(this.event_handler.bind(this), 1000);
Andy Barron
  • 246
  • 2
  • 9
1

setInterval() doesn't work with this. See here: https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval#The_this_problem

Code executed by setInterval() is run in a separate execution context to the function from which it was called. As a consequence, the this keyword for the called function will be set to the window (or global) object, it will not be the same as the this value for the function that called setTimeout.

You can call your method inside an anonymous function like this:

function job_queue(){
    this.job = null;
    this.pointer = this.job;
    this.job_dispatcher = null; 
    this.length = 0;
}
job_queue.prototype.add_job = function( job ){
    if( this.job == null ){
        console.log('1st');
        this.job = {
            job:job,
            next:null
        };
        this.pointer = this.job;
        this.length = 1;
    }else{
        console.log('2nd');
        this.pointer.next = { 
            job:job,
            next:null
        };
        this.pointer = this.pointer.next;
        this.length++;
    }
};

job_queue.prototype.event_handler = function(){
        
        if( typeof this.job['job'] == 'undefined'){
            console.log('??');
        }
        if( this.job.job != null ){
            console.log('hi');
            this.job.job();
            this.job = this.job.next();
        }

}

job_queue.prototype.start_dispatch = function(){
    var self = this;
    if( this.job_dispatcher == null ){
        console.log( this.event_handler );
        this.job_dispatcher = setInterval( function(){self.event_handler()},1000);
    }
}



var jq = new job_queue();
function a(){
    console.log('hi');
};
function b(){
    console.log('hi2');
}
jq.add_job(a);
jq.add_job(b);
jq.add_job(a);
jq.start_dispatch();
Julien Grégoire
  • 16,864
  • 4
  • 32
  • 57
  • Thank you. Is there a better way to write a job queue without having setInterval? – Jian Sep 04 '15 at 18:16
  • And also in the link this.job_dispatcher = setInterval( function(){self.event_handler()},1000); Why calling self.event_handler() without wrapping into closure doesn't work? – Jian Sep 04 '15 at 18:24
  • I'm not sure what would be the best approach for a queue. Maybe have a look at promises. Or you could simply have an array in which you push the jobs and call your event handler in add_job. In any case, I don't think you need setInterval. And as for why calling self.event_handler only works in the anonymous function: the problem isn't the 'this' in the argument of setInterval, it's the 'this' that is being assigned when executing the function. It's only running the function you provide and assigning window as this. – Julien Grégoire Sep 05 '15 at 15:58
0

If you console.log(this) in the event_handler, you'll see that this points to the Window object. This is because it is being called in setInterval.

job_queue.prototype.event_handler = function(){
        console.log(this); // <--- A Window object
        if( typeof this.job['job'] == 'undefined'){
            console.log('??');
        }
        if( this.job.job != null ){
            console.log('hi');
            this.job.job();
            this.job = this.job.next();
        }
}

One workaround for it would be to store the job_queue reference as self, and then call setInterval as follows:

var self = this;
this.job_dispatcher = setInterval(function() {
     self.event_handler();
}, 1000);

Code snippet:

function job_queue(){
    this.job = null;
    this.pointer = this.job;
    this.job_dispatcher = null; 
    this.length = 0;
}
job_queue.prototype.add_job = function( job ){
    if( this.job == null ){
        console.log('1st');
        this.job = {
            job:job,
            next:null
        };
        this.pointer = this.job;
        this.length = 1;
    }else{
        console.log('2nd');
        this.pointer.next = { 
            job:job,
            next:null
        };
        this.pointer = this.pointer.next;
        this.length++;
    }
};

job_queue.prototype.event_handler = function(){
        console.log(this);
        if( typeof this.job['job'] == 'undefined'){
            console.log('??');
        }
        if( this.job.job != null ){
            console.log('hi');
            this.job.job();
            this.job = this.job.next();
        }

}

job_queue.prototype.start_dispatch = function(){
    if( this.job_dispatcher == null ){
        console.log( this.event_handler );
        var self = this;
        this.job_dispatcher = setInterval(function() {
             self.event_handler(self);
        },1000);
    }
}



var jq = new job_queue();
function a(){
    console.log('hi');
};
function b(){
    console.log('hi2');
}
jq.add_job(a);
jq.add_job(b);
jq.add_job(a);
jq.start_dispatch();
Community
  • 1
  • 1
John Bupit
  • 10,406
  • 8
  • 39
  • 75
  • Thank you. I'm new to the javascript and had no idea how to use some of the function properly. – Jian Sep 04 '15 at 18:15