Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Server-side setInterval/clearInterval with Meteor

I'm creating a Meteor app that has some simple timers in it. Pressing the start or stop buttons on a timer each call a method to set or clear an interval timer, among other things. When I setInterval, I store the resulting object in the current timer document so that it is easy to find later when I want to clear the interval timer. This is where I'm running into issues.

When running Meteor.setInterval() server-side, it returns an object. According to the node.js docs, this is normal. If I log the resulting object after creation, it returns this:

{ _idleTimeout: 5000,
  _idlePrev: 
   { _idleNext: [Circular],
     _idlePrev: 
      { _idleTimeout: 5000,
        _idlePrev: [Object],
        _idleNext: [Circular],
        _idleStart: 1393271941639,
        _onTimeout: [Function],
        _repeat: false },
     msecs: 5000,
     ontimeout: [Function: listOnTimeout] },
  _idleNext: 
   { _idleTimeout: 5000,
     _idlePrev: [Circular],
     _idleNext: 
      { _idleTimeout: 5000,
        _idlePrev: [Circular],
        _idleNext: [Object],
        _idleStart: 1393271941639,
        _onTimeout: [Function],
        _repeat: false },
     _idleStart: 1393271941639,
     _onTimeout: [Function],
     _repeat: false },
  _idleStart: 1393271943127,
  _onTimeout: [Function: wrapper],
  _repeat: true }

If I log the object after retrieving it from my document, I get this:

{ _idleTimeout: 5000,
  _idlePrev: null,
  _idleNext: null,
  _idleStart: 1393271968144,
  _repeat: true }

So, using clearInterval with this does not work. Here is my server-side code:

Meteor.methods({

    play: function(entry){ //entry is the document
         var currentPlayTimer = entry; //Global variable for the interval timer
         Entries.update({_id: currentPlayTimer._id},{$set:{playing:true}}); //This is mostly to set the status of the play button for the client
         var IntervalId = Meteor.setInterval(function(){Entries.update({_id: currentPlayTimer._id},{$inc:{time:1},$set:{intervalId: IntervalId}});},5000); //Increment by 1 every 5 seconds, put the object from the interval timer into the current document
         console.log(IntervalId);
    },

    stop: function(entry){ //entry is the document
         var currentPlayTimer = entry;
         IntervalId = currentPlayTimer.intervalId;
         console.log(IntervalId);
         Meteor.clearInterval(IntervalId);
         Entries.update({_id: currentPlayTimer._id},{$set:{playing:false, intervalId: null}});

    }
});

Also, you will notice that in the play method, I set intervalId inside of the setInterval function. I tried this in desperation, and it worked. For some reason, if I try to update the document right after creating the interval timer with Entries.update({_id: currentPlayTimer._id},{$set:{intervalId: IntervalId}}), it fails.

All of this worked great as client-side code, but I need this to be done server side. I want the timer to keep going at the correct pace whether you have the page open on 5 devices or none.

Thanks for your help! This project is my first using Meteor or anything on Node, and I am really enjoying it so far.

like image 522
dotbat Avatar asked Feb 24 '14 20:02

dotbat


People also ask

Can I clearInterval in setInterval?

It's worth noting that the pool of IDs used by setInterval() and setTimeout() are shared, which means you can technically use clearInterval() and clearTimeout() interchangeably.

How do you break a setInterval loop?

The setInterval() function is commonly used to set a delay for functions that are executed again and again, such as animations. You can cancel the interval using clearInterval() . If you wish to have your function called once after the specified delay, use setTimeout() .

What is the purpose of the clearInterval method?

The clearInterval() method clears a timer set with the setInterval() method.

What is the correct syntax to call the setInterval () function?

The setInterval method has the same syntax as setTimeout : let timerId = setInterval(func|code, [delay], [arg1], [arg2], ...)


1 Answers

This is essentially down to two issues: firstly, that the implementations of setInterval and clearInterval are different on the client (at least in Chrome) and in Node, and secondly that you can't serialise functions in BSON, which means that all your methods, and properties containing methods, are being dropped from the object when you try to insert it as a document on the server. That's why the object you subsequently retrieve is so much simpler/smaller, and why you can't pass it to clearInterval, as it's missing most of the required information.

If you log the return value of setInterval in the client, you'll notice that it's just an integer, which can of course be serialised, and so you get exactly the same thing out of the MongoDB as you put in, and clearInterval works fine. I am no expert at all on the client implementation of set... and clearInterval, but frankly a four-digit integer doesn't seem particularly robust for this purpose, although it does have some advantages that you've identified.

In summary, I don't think you're going to be able to go about things the way in which you're attempting on the server, unless somebody else can think of a smart way of serialising the part of the interval object which is required for clearing it and rebuilding an appropriate object once it's been retrieved, but this requires more knowledge of Node than I possess. Otherwise, I think you have two options:

  • Simply store your intervals in memory in some kind of vanilla javascript object.
  • Use an alternative object for timing which would allow you to store a serialisable identifier. I have used the meteor-cron package in the past, albeit briefly, and it may be worth a look to see if this is possible using that package or some derivative thereof.
like image 60
richsilv Avatar answered Nov 12 '22 18:11

richsilv