#107 ✓not_for_core
disccomp

PeriodicalExecuter pause/restart

Reported by disccomp | August 25th, 2008 @ 08:07 AM

I needed pause/restart for one of my projects. Someone wrote an extension once for this purpose, but the link was dead. And thanks for all your hard work! -Mark

P.S. I'm sure this code could be greatly improved by you prototype gurus.


var PeriodicalExecuter = Class.create({
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;
    this.lastExec = this.now();
    this.paused = false;
    this.pausedAt = 0;
    this.accumTimePaused = 0;
    this.registerCallback();
  },
  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this),
    this.frequency * 1000);
  },
  execute: function() {
    this.callback(this);
  },
  pause: function(){
    if(this.timerRescheduler>0){clearTimeout(this.timerRescheduler)}
    if (this.paused) return;
    this.stop();
    this.paused = true;
    this.pausedAt = this.now();
    return true;
  },
  timeLeft: function(){
    if(this.paused){
      return this.frequency - (this.pausedAt - this.accumTimePaused -
this.lastExec);
    }else if(this.timer==null){
      return null;
    }else{
      return this.frequency - (this.now() - this.accumTimePaused -
this.lastExec);
    }
  },
  restart: function(){
    if (this.paused){
      if(this.timerRescheduler>0){clearTimeout(this.timerRescheduler)}
      this.timerRescheduler =
setTimeout(this.reJobCallback.bind(this), this.timeLeft()*1000);
      this.accumTimePaused += this.now() - this.pausedAt;
    }else if(this.timer == null){
      this.registerCallback();
    }
    this.pausedAt = null;
    this.paused = false;
  },
  reJobCallback : function(){
    this.onTimerEvent();
    this.registerCallback();
    this.accumTimePaused = 0;
  },
  now: function(){ return new Date().getTime() / 1000;},
  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },
  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.execute();
      } finally {
        this.currentlyExecuting = false;
        this.lastExec = this.now();
      }
    }
  }
 @@@

Comments and changes to this ticket

  • John-David Dalton

    John-David Dalton August 25th, 2008 @ 04:02 PM

    • State changed from “new” to “enhancement”
    • Tag set to function
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 08:58 PM

    • Tag changed from function to base, class, needs_patch

    This is a less obtrusive (to Prototype) way of gaining this functionality. It builds on the stop() method and adds a resume() method. If this looks good to others, I can make a patch.

    
    var PeriodicalExecuter = Class.create({
      initialize: function(callback, frequency) {
        this.callback = callback,
         this.frequency = frequency,
         this.currentlyExecuting = false,
         this.lastStart = new Date().getTime(),
         this.resumeAt = null;
        this.registerCallback();
      },
    
      registerCallback: function() {
        this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
      },
    
      execute: function() {
        this.callback(this);
      },
    
      stop: function() {
        if (!this.timer) return;
        clearInterval(this.timer);
        this.timer = null;
        this.resumeAt = (new Date().getTime() - this.lastStart) % (this.frequency * 1000);
      },
    
      resume: function(clearReset) {
        if (clearReset) {
          this.lastStart = new Date().getTime();
          this.resumeAt = this.frequency * 1000;
        }
    
        if (this.timer == null)
          this.timer = setInterval(this.onTimerEvent.bind(this), this.resumeAt);
      },
    
      onTimerEvent: function() {
        if (!this.currentlyExecuting) {
          try {
            this.currentlyExecuting = true;
            this.execute();
          } finally {
            this.currentlyExecuting = false;
          }
        }
      }
    });
    
  • Mark Caudill

    Mark Caudill August 25th, 2008 @ 09:09 PM

    I just noticed one small error in this: this.resumeAt should default to frequency, not null (although I don't think null could ever logically reach the 2nd setInterval)

  • Radoslav Stankov

    Radoslav Stankov August 26th, 2008 @ 10:14 AM

    I think it will be good to add subscribe and unsubscribe methods too. My idea is with one PeriodicalExecuter(and one Interval) to be able to call multiple functions.

  • Mark Caudill

    Mark Caudill August 26th, 2008 @ 03:43 PM

    @Radoslav,

    Why add subscribe/unsubscribe? It seems somewhat redundant to the concept to me. I do understand the benefits, but most of them can be achieved by an anonymous function (without the overhead).

  • Radoslav Stankov

    Radoslav Stankov August 26th, 2008 @ 04:43 PM

    Recently for a project of mine, I had one several independed actions who needed to be excecuted on one interval of time. The preformace was very low, because I had a lot of tasks(10 and more), then I needed to be able to stop one executer and to start it again.

    So I created an extend version of PeriodicalExcecutor with subscribe / unsubscribe methods and it worked very well.

  • Mark Caudill

    Mark Caudill August 26th, 2008 @ 05:20 PM

    Here's some test code as an example using an anonymous function. I kind of wonder what others think on the matter of adding subscribe/unsubscribe as described by Radoslav (as to whether this idea would create unneeded overhead for simple PEs).

    
    var todo = [];
    somefunction = function() {
      for (var i=0,j=todo.length; i<j; i++) {
        try {
          (todo[i])();
        }
        catch (e) {}
      }
    }
    dosomething = function() {
      alert('hi');
    }
    
    somepe = new PeriodicalExecutor(somefunction, 10);
    
    todo.push('dosomething');
    
  • sam.hendley

    sam.hendley January 28th, 2009 @ 02:09 AM

    I just want to bump this as an issue. I needed behavior like the ones listed but I made the frequency configurable. The main use-case I have is that I allow the user to control the polling speed by letting them reset the frequency without reloading the page. Very handy! I think you were right Mark, there really isn't a need for a subscribe/unsubscribe functionality here, you can pass in a delegating function/object that can be responsible for that behavior.

    My implementation is below and there is a pastie at http://pastie.org/372778 with the diff.

    
    var PeriodicalExecuter = Class.create({
      initialize: function(callback, frequency) {
        this.callback = callback;
        this.currentlyExecuting = false;
    
        this._setFrequency(frequency);
      },
    
      execute: function() {
        this.callback(this);
      },
    
      _setFrequency: function(frequency){
        if(this.timer || frequency != this.frequency){
          clearInterval(this.timer);
          this.timer = null;
        }
    
        if(frequency > 0){
          this.timer = setInterval(this.onTimerEvent.bind(this), frequency * 1000);
          this.frequency = frequency;
        }
      },
    
      stop: function() {
        this._setFrequency(0);
      },
    
      start: function(frequency){
        if(Object.isUndefined(frequency)) frequency = this.frequency;
        this._setFrequency(frequency);
      },
    
      onTimerEvent: function() {
        if (!this.currentlyExecuting) {
          try {
            this.currentlyExecuting = true;
            this.execute();
          } finally {
            this.currentlyExecuting = false;
          }
        }
      }
    });
    
  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 01:55 AM

    • Tag changed from base, class, needs_patch to needs_patch, section:lang

    [not-tagged:"class" tagged:"section:lang" bulk edit command]

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 02:28 AM

    • Tag changed from needs_patch, section:lang to missing:patch, section:lang

    [not-tagged:"needs_patch" tagged:"missing:patch" bulk edit command]

  • Tobie Langel

    Tobie Langel July 24th, 2009 @ 03:37 AM

    • Tag changed from missing:patch, section:lang to needs:patch, section:lang

    [not-tagged:"missing:patch" tagged:"needs:patch" bulk edit command]

  • T.J. Crowder

    T.J. Crowder November 16th, 2009 @ 04:50 PM

    [responsible:none bulk edit command]

  • Franco Monsalvo

    Franco Monsalvo March 31st, 2010 @ 11:40 PM

    • State changed from “enhancement” to “not_for_core”
  • meruveruki001 (at outlook)

    meruveruki001 (at outlook) November 28th, 2018 @ 03:35 PM

    cheat engine android no root Xmodgames apk allows users to download games and apps from different game developers. There are different mods available by which the ...

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile ยป

The Prototype JavaScript library.

Shared Ticket Bins

Referenced by

Pages