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

  • viovio25

    viovio25 December 25th, 2018 @ 03:56 PM

    go to this page Ethernet and internet both sometimes can make you confuse if you are not a frequent user of the internet. You may be well versed with both the ...

  • viovio26

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