HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Inactivity Timeout

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
timeoutinactivitystackoverflow

Problem

I made this piece of code to detect inactivity on different aspects of my app. It is part of a set of pieces to analyze user behavior.

Not saying it's ugly, but good looking constructive criticism to help me improve my code and could be useful to me for other similar components.



function InactivityTimeout(idle_time, callback) {
this.state = 0; // 0-new, 1=active, 2=idle
this.idle_time = idle_time;
this.callback = callback;
this.start();
}

InactivityTimeout.prototype.start = function() {
this.state = 1;
this.timer = setTimeout(this.timeout.bind(this), this.idle_time);
}

InactivityTimeout.prototype.activity = function() {
if (this.state == 1) {
clearTimeout(this.timer);
}
this.start();
}


InactivityTimeout.prototype.timeout = function() {
this.state = 2;
this.callback();
}

/// usage

var timer=new InactivityTimeout(5000, function() {
alert("idle reached");
});

var el = document.getElementById('btn');
el.onclick = function() {
timer.activity();
}

Foo

Solution

You could pass in an object into the constructor instead of using arguments. That way, you don't have to mind order. Additionally, you could add in defaults:

function InactivityTimer(options){

  // Defaults
  this.defaults = {
    timeout : 10
  };

  // Merge options to defaults> Let's just say you use jQuery.
  this.options = $.extend({}, this.defaults, this.options);

}


Now you wouldn't want to make this code run all the time, so I suggest you add in a start as well as a stop. You could also add in an autostart in options so it starts as soon as an instance is created.

InactivityTimer.prototype.start = function(){
  if(this.timer) return;
  this.timer = setTimeout(function(){
    ...
  },this.options.timeout);
}
InactivityTimer.prototype.stop = function(){
  clearTimeout(this.timer);
  this.timer = null;
}


You wouldn't want to hard-code all the code that runs when activity or inactivity occurs. I suggest you extend from an EventEmitter object. I have a simple implementation of such, which you could just plug in. Now you emit events in your code, and code outside can listen.

function InactivityTimer(options){
  EventEmitter.call(this); // Inherit properties
  ...
}

InactivityTimer.prototype = new EventEmitter();
InactivityTimer.prototype.start = function(){
  if(this.timer) return;
  this.timer = setTimeout(function(){
    this.emit('idle');
  },this.options.timeout);
  this.emit('start');
}
InactivityTimer.prototype.stop = function(){
  clearTimeout(this.timer);
  this.timer = null;
  this.emit('stop');
}

var idleCheck = new InactivityTimer({...});

idleCheck.on('start',function(){
  // timer started
});
idleCheck.on('stop',function(){
  // timer stopped
});
idleCheck.on('idle',function(){
  // user became idle
});
idleCheck.on('active',function(){
  // user became active
});


The same with listening for events instead of hard-coding the code that reacts to the events into InactivityTimer, you'd might want to also extract the code that listens for activity. Activity can be anything, and the consumer of your code might consider only click or probably mouse movement etc. Suggesting you add observe to listen for certain activity.

InactivityTimer.prototype.observe = function(observer){
  var instance = this;

  // Call the observer, passing it a callback that should be called when an event happens
  observer.call(this, function(){
    clearTimeout(instance.timer); // clear the timer
    instance.emit('active');      // inform listeners that timer became active
    instance.start                // start another timer
  });
});

// Usage
var timer = new InactivityTimer({...});

timer.observe(function(activate){
  $('body').on('click',activate); // Observe body clicks
  $(window).on('scroll',activate); // Observe scrolling
});

Code Snippets

function InactivityTimer(options){

  // Defaults
  this.defaults = {
    timeout : 10
  };

  // Merge options to defaults> Let's just say you use jQuery.
  this.options = $.extend({}, this.defaults, this.options);

}
InactivityTimer.prototype.start = function(){
  if(this.timer) return;
  this.timer = setTimeout(function(){
    ...
  },this.options.timeout);
}
InactivityTimer.prototype.stop = function(){
  clearTimeout(this.timer);
  this.timer = null;
}
function InactivityTimer(options){
  EventEmitter.call(this); // Inherit properties
  ...
}

InactivityTimer.prototype = new EventEmitter();
InactivityTimer.prototype.start = function(){
  if(this.timer) return;
  this.timer = setTimeout(function(){
    this.emit('idle');
  },this.options.timeout);
  this.emit('start');
}
InactivityTimer.prototype.stop = function(){
  clearTimeout(this.timer);
  this.timer = null;
  this.emit('stop');
}


var idleCheck = new InactivityTimer({...});

idleCheck.on('start',function(){
  // timer started
});
idleCheck.on('stop',function(){
  // timer stopped
});
idleCheck.on('idle',function(){
  // user became idle
});
idleCheck.on('active',function(){
  // user became active
});
InactivityTimer.prototype.observe = function(observer){
  var instance = this;

  // Call the observer, passing it a callback that should be called when an event happens
  observer.call(this, function(){
    clearTimeout(instance.timer); // clear the timer
    instance.emit('active');      // inform listeners that timer became active
    instance.start                // start another timer
  });
});

// Usage
var timer = new InactivityTimer({...});

timer.observe(function(activate){
  $('body').on('click',activate); // Observe body clicks
  $(window).on('scroll',activate); // Observe scrolling
});

Context

StackExchange Code Review Q#75407, answer score: 4

Revisions (0)

No revisions yet.