JS variable containing ongoing time value - javascript

I'm working on a chatbot script (Hubot - running in terminal) exercise and looking for a method to count the time since the last message was left in the thread. Then after nobody has left a message for X number of minutes (let's say 10,000 milliseconds) I would like to console.log("CRICKETS!..CRICKETS!..")
I'm imagining something like:
//currentTime - startTime = timeSince
//and
// if( timeSince > 10,000)
// {console.log("Crickets!..")
however I'm not sure of how to create the currentTime variable as continuously growing counter
Below is the code I've started which doesn't appear to throw any errors in the , but also doesn't seem to work as I'm running it in the terminal. It just prints the current time twice
module.exports = function(robot) {
return robot.hear(/$/i, function(msg) {
var startTime = (Date.now()) ;
return(startTime);
if (Date.now() - startTime > 1000) {
console.log("CRICKETS..!...")
};
});
};
You'll notice I'm using Date.now() but I'm not attached if there's a better method. Also here is a link to basic hubot scripts in case it is needed for context - https://github.com/github/hubot/blob/master/docs/scripting.md

You can always use setTimeout and cancel it if need be
Pseudo-code:
var myTimeout = setTimeout(function () {
//nobody has left a message for 10 seconds
}, 10000);
if (user has left message)
clearTimeout(myTimeout);

The window.setTimeout function allows you to trigger a callback function after a delay. And you can clear that timeout by calling window.clearTimeout(value_returned_by_setTimeout).
We could define a callback: function crickets(){ console.log('Chirp! Chirp!'); }
Assuming some function newMessage gets called whenever a a new message appears, you could try something like this:
var cricketTimeout = null;
function newMessage(){
//... your code
if (cricketTimeout) clearTimeout(cricketTimeout);
cricketTimeout = setTimeout(crickets, delayInMilliseconds);
}

Related

Only execute a function once in a given amount of time

I have an api request that is called multiple times in a given amount of time. More specifically this request is for refreshing the user token, so it's called on every request, which adds up pretty quickly. I would like to create a function that tells the function not to run for a given amount of seconds. I have tried using lodash debounce but I can't get it to work.
let debounceRefresh;
debounceRefresh = debounce(() => {
api.request(){
});
}, 1000);
debounceRefresh();
Am I executing this wrong? Is it possible to do?
Yes, you definitely need throttle for the job.
// in this example we invoke a fn for a period of 10 sec, invoking it 2 times a second, but we can perceive that the original function is only invoked at most once per 2 seconds according to the parameter below:
var TOTAL_TIME_TO_RUN = 10000; // 10 sec
var THROTTLE_INTERVAL = 2000; // <= adjust this number to see throttling in action
var INVOCATION_INTERVAL = 500; // 0.5 sec
// regular fn
var punchClock = function punchClock() {
console.log(new Date().toISOString() + ' - call api');
};
// wrap it and supply interval representing minimum delay between invocations
var throttledPunchClock = _.throttle(punchClock, THROTTLE_INTERVAL);
// set up looping
var intervalId = setInterval(function() {
console.log("attempting call api");
throttledPunchClock()
}, INVOCATION_INTERVAL);
// run the demo
setTimeout(() => clearInterval(intervalId), 10000)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
<pre>
var TOTAL_TIME_TO_RUN = 10000; // 10 sec
var THROTTLE_INTERVAL = 2000; // < = adjust this number to see throttling in action
var INVOCATION_INTERVAL = 500; // 0.5 sec
</pre>
Snippet from github
Have you tried with a timeout?
const myTimeout = setTimeout(debounceRefresh, 1000);
If the function is called again, you can clear the timeout and reset it
clearTimeout(myTimeout);
Why don't you use a different listener? Perhaps when data is received?

multiple setInterval timers on node server

My app is a game where a user has 30 mins to finish....node backend
Each time a user starts a game then a setInterval function is triggered server side....once 30mins is counted down then I clearInterval.
How do I make sure that each setInterval is unique to the particular user and the setInterval variable is not overwritten each time a new user starts a game? (or all setInterval's are cleared each time I clear).
Seems like I might need to create a unique "interval" variable for each new user that starts game??
Below code is triggered each time a new user starts a game
let secondsLeft = 300000;
let interval = setInterval(() => {
secondsLeft -= 1000;
if (secondsLeft === 0) {
console.log("now exit");
clearInterval(interval);
}
}, 10000);
Thanks!!
We used agenda for a pretty big strategy game backend which offers the benefit of persistence if the node app crashes etc.
We incorporated the user id into the job name and would then schedule the job, along with data to process, to run at a determined time specifying a handler to execute.
The handler would then run the job and perform the relevant tasks.
// create a unique jobname
const jobName = `${player.id}|${constants.events.game.createBuilding}`;
// define a job to run with handler
services.scheduler.define(jobName, checkCreateBuildingComplete);
// schedule it to run and pass the data
services.scheduler.schedule(at.toISOString(), jobName, {
id: id,
instance: instance,
started: when
});
Worked pretty well and offered decent protection against crashes. Maybe worth considering.
First: Concurrent Intervals and Timers are not the best design approach in JS, it is better to use one global timer and a list of objects storing the start, end, userid etc and update these in a loop.
Anyway. To have your interval id bound to a certain scope, you can use a Promise like so:
const createTimer = (duration, userid) => new Promise(res => {
const start = new Date().getTime();
let iid;
(function loop () {
const
now = new Date().getTime(),
delta = now - start
;
//elapsed
if (delta >= duration) {
clearTimeout(iid);
res(userid);
//try again later
} else {
iid = setTimeout(loop, 100)
}
})();
});
This way each timer will run »on its own«. I used setTimeout here since that wont requeue loop before it did everything it had to. It should work with setInterval as well and look like that:
const runTimer = (duration, userid, ontick) => new Promise(res => {
const
start = new Date().getTime(),
iid = setInterval(
() => {
const delta = new Date().getTime() - start;
if (delta < duration) {
//if you want to trigger something each time
ontick(delta, userid);
} else {
clearInterval(iid);
res(userid);
}
}, 500)
;
});
You do not even need a promise, a simple function will do as well, but then you have to build some solution for triggering stuff when the timer is elapsed.
Thanks #Chev and #philipp these are both good answers.
I was also made aware of a technique where you use an array for the setInterval variable.....this would make my code as follows;
let intervals = []
let secondsLeft = 300000;
intervals['i'+userId] = setInterval(() => {
secondsLeft -= 1000;
if (secondsLeft === 0) {
console.log("now exit");
clearInterval(interval);
}
}, 10000);
Does anyone else foresee this working?.
UPDATE 6.56pm PST.....it works!!

requestAnimationFrame running before being called by function

I am attempting to make a simple timer (counting up from 0) with JavaScript and requestAnimationFrame. I want to start the timer from 0 when something is clicked. Currently my code displays the timer when the button is clicked, but it looks to me like requestAnimationFrame is running before the function is even called. If you load the code on a web page and wait a few seconds, then click the button, you will see the timer doesn't begin at 0, it starts at however many seconds it has been since the page first loaded. I'm at a loss and googling has not helped me figure out why/how the timer is starting counting before the function has been called.
My current code:
<div class="time">
Time: <label id="labelTime"></label>
</div>
<button id="button">Click me</button>
<script>
const button = document.getElementById('button');
button.addEventListener('click', clickButton);
function clickButton() {
runTimer();
}
function runTimer() {
let rAF_ID;
let rAFCallback = function(callback) {
let count = callback;
let s = Math.floor((count / 1000) % 60).toString().padStart(2, '0');
let m = Math.floor((count / 60000) % 60);
document.getElementById('labelTime').innerHTML = m + ":" + s;
rAF_ID = requestAnimationFrame(rAFCallback);
}
rAF_ID = requestAnimationFrame(rAFCallback);
}
</script>
The timestamp (DOMHighResTimeStamp) value passed into your rAFCallback function does not start from when the animation was first run, instead it has a "time origin" which varies on the context.
https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp
If the script's global object is a Window, the time origin is determined as follows:
If the current Document is the first one loaded in the Window, the time origin is the time at which the browser context was created.
If during the process of unloading the previous document which was loaded in the window, a confirmation dialog was displayed to let the user confirm whether or not to leave the previous page, the time origin is the time at which the user confirmed that navigating to the new page was acceptable.
If neither of the above determines the time origin, then the time origin is the time at which the navigation responsible for creating the window's current Document took place.
If the script's global object is a WorkerGlobalScope (that is, the script is running as a web worker), the time origin is the moment at which the worker was created.
In all other cases, the time origin is undefined.
So if you want to get a delta-time value from when the animation was started, you'll need to do that yourself, like so:
let timestampAtStart = null;
let lastRequestId = null;
function myAnimateFunction( timestamp ) {
if( !timestampAtStart ) {
timestampAtStart = timestamp;
}
let timeSinceStart = timestamp - timestampAtStart;
console.log( timeSinceStart );
lastRequestId = window.requestAnimationFrame( myAnimateFunction );
}
function startAnimation() {
if( lastRequestId ) window.cancelAnimationFrame( lastRequestId );
timestampAtStart = null;
lastRequestId = window.requestAnimationFrame( myAnimateFunction );
}

Console output one time every 40 seconds with interval doesn't work

I'm trying to use Twitter API in order to retweet.And because Twitter has limitation to 2400 actions per day I decided to limit the retweet to one time every 40 seconds.
I'm using https://www.npmjs.com/package/twit using Streaming API.
My problem is that it continuously streams console.log instead of using setInterval.
By stream I mean it outputs console.log multiple times instead of one time.
function hastagRetweet() {
var stream = T.stream('statuses/filter', { track: ['#hastag']})
stream.on('tweet', function (tweet,error) {
var retweetId = tweet.id; // id
var retweetId_str = tweet.id_str;
var tweetTextRetweet = tweet.text; // tweet text
function twInterval() {
console.log('INFO ------- ',tweet.id);
console.log('INFO ------- ',tweet.text);
};
setInterval(twInterval, 1000 * 40);
});
}
Any way of getting the variales data retweetId,retweetId_str,tweetTextRetweet outside stream.on beside inserting them into a mysql table so that every 40 seconds it checks the variables fixed data and executes console.log?
There are multiple problems here:
You're trying to start an interval timer in an event handler. That means that every time the event fires you would be attempting to start a new interval timer and they would pile up.
Even if you did sucessfully start up an interval timer, each one would never change it's output since the variables in scope for it never change as it is started up within a given function and the arguments to that function are what they were when it was first called. Subsequent calls of the function will start a new function not change the arguments on the prior call of the function.
You aren't even starting your interval correctly. As it is, all you're doing is calling the function and passing it's return value to setInterval() which does nothing.
If the goal is just to output to the console each stream tweet event, then probably what you want is just this:
function hastagRetweet() {
var stream = T.stream('statuses/filter', { track: ['#hastag']})
stream.on('tweet', function (tweet,error) {
var retweetId = tweet.id; // id
var retweetId_str = tweet.id_str;
var tweetTextRetweet = tweet.text; // tweet text
console.log('INFO ------- ',tweet.id);
console.log('INFO ------- ',tweet.text);
});
}
You cannot get these variables outside the .on() handler. It's an async callback and the only place they reliably exist is within that handler.
If you can describe in more detail what the end result you're trying to achieve is, we can likely help more specifically. If you want to do something every 40 seconds, then maybe you need to collect data in some collection (probably an array) and then every 40 seconds evaluate what you have just recently collected.
Here's a method of collecting the events in an array and then evaluating them every 40 seconds:
function hastagRetweet() {
var stream = T.stream('statuses/filter', { track: ['#hastag']});
var tweets = [];
stream.on('tweet', function (tweet,error) {
tweets.push({id: tweet.id, str: tweet.id_str, text: tweet.text});
});
setInterval(function() {
// evaluate the tweets in the tweets array every 40 seconds
// do something with them
for (var i = 0; i < tweets.length; i++) {
// tweets[i].id
// tweets[i].str
// tweets[i].text
}
// reset the tweets array
tweets.length = 0;
}, 40 * 1000);
}
Please note that once you call hastagRetweet(), it will run forever.

InDesign CS5 Script: How can I open and close a popup window after `n` seconds?

The window is opening fine, but the script will not continue until the popup window is manually closed! This is not desirable, as I would rather the window close itself after n seconds...
So do I have to open the window in a separate thread from the rest of the script? Is that even possible?
Here's my code so far:
function showMessageWindow()
{
var script = getScriptName(); // initialized from the function below
var message = new Window("dialog", script);
message.add("StaticText", undefined, "The script " + script + " is now running, please wait...");
message.show();
var startTime = new Date().getTime(); // in milliseconds
var currentTime = new Date().getTime(); // in milliseconds
var waitTime = 5000; // 1000 milliseconds is 1 second
var delay = function()
{
while( (currentTime - startTime) < waitTime)
{
currentTime = new Date().getTime(); // in milliseconds
}
} // end of closure delay
delay(); // calling the delay closure function here
message.close(); // close the message after the specified delay time
} // end of function showMessageWindow
// called from the function showMessageWindow
function getScriptName()
{
var scriptName = "";
try
{
scriptName = File(app.activeScript).name;
}
catch(e)
{
scriptName = File(e.fileName).name;
}
return scriptName;
} // end of function getScriptName
dialog type windows are modal dialogs preventing any background operations. However even with a non modal window, I am not sure you can get parallel execution of both routines from teh same script. I am pretty sure teh script engine will kindly wait for yt-our delaying routine to end before keeping on moving :\
The only way I could deal with such asynchronous processus was using scriptUI in combination of a swf file and have any timer stuff done in AS3. That way you can have script execution to move on within InDesign and have your loop running in the swf file. I did that once especially to monitor a hot folder.
BTW : you have a mistake in your code => message.add("StaticText", undefined,…
That should be statictext in lowercase ;)

Categories