Dynamic time interval to call a function node.js - node.js

I have divided a day on 6 parts and each part of a day has its own interval. My function must run in this intervals.
for example 12:00pm - 15:00pm interval is 10 min, so function must be called every 10 min, but 15:00pm - 20:00pm interval is 2 min so from 15:01pm interval must be changed from 10min to 2min.
Need a suggestion how to build this kind of timer.
I can get intervals from mongoDB or local json file.
I guess I have to check what time is it and get an interval (from mongoDB or json file) for that time, then pass it to setInterval() or Cron job scheduler.
Tryed this way but every time im passing new interval last intervals are still working: If interval is 2 min and im changing it to 5 min, function is called twice: every 2 min and every 5 min in both setInterval() and Cron
const test = (min) => {
console.log(min);
var intervale = setInterval(() => {
console.log('firing', min, new Date());
}, min);
}
const test = (min) => {
cron.schedule(`0 */${min} * * * *`, () => {
console.log('firing cron', new Date())
});
}
thank you

Can you do something like this? This uses the EventEmitter model and fires an event based on the custom Interval.
const eventemitter_cls = require("events").EventEmitter;
let eventemitter = new eventemitter_cls();
intervalName = "randomintervals"
let sumOfTwoNumbers = () => {
console.log(`hello world ${(new Date()).getMinutes()} ${(new Date()).getSeconds()}` );
// After you are done with your function, again you need to emit the event based on the time interval
setTimeout(() => {
eventemitter.emit(intervalName)
}, getTimeInterval());
}
let getTimeInterval = () => {
let currentTime = new Date();
// put your complicated interval schedule here
if(currentTime.getHours() >= 10 && currentTime.getHours() < 11){
return 5000;
}
else if (currentTime.getHours() > 12 && currentTime.getHours() < 15) {
return 2000;
}
else{
return 1000;
}
}
// give your function name here. the function will be called whenever someone emits intervalName
eventemitter.on(intervalName, sumOfTwoNumbers);
eventemitter.emit(intervalName);

I think It's still firing because there is no clearInterval telling it to stop.

Related

is posible sleep in Azure Function Nodejs?

I have calculated Request Units (RU) needed for 50 update operations per seconds on:
https://cosmos.azure.com/capacitycalculator/
but those 50 update operations need 1 second to complete
Thus,
I need to sleep 1 second while inserting into a mongodb database with bulk operation (group of update operations)
is this posible in azure functions with nodejs?
I have tried this code
sleep(milliseconds) {
return new Promise(resolve => setTimeout(resolve, milliseconds))
}
but doesnt work.
any similar situation?
Usually we use delay async function for waiting a block to rerun after 1000 milliseconds.
Below is one of the sample we make us of it on our regular basis to add delay to our logic. As you've not provided your entire code, make sure you are following the below structure.
function RunAfterOneSec() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 1000);
});
}
async function asyncCall() {
console.log('calling');
const result = await RunAfterOneSec();
console.log(result);
// expected output: "resolved"
}
asyncCall();
Or you can use with help of node sleep package and we can use it as below:
var sleep = require('sleep');
sleep.sleep(n)
sleep.sleep(n): sleep for n seconds
sleep.msleep(n): sleep for n miliseconds
sleep.usleep(n): sleep for n microseconds (1 second is 1000000 microseconds)

node: run function consistently X times per second while tracking how long it runs

I want to write a system that automates max. 8192 different values consistently 40 times per second. The kind of automation, I want to do is e.g. letting some values form a sinus from 0 to 255, others ramp up from 50 to 80, others switch between 255 and 0 etc.
I need to know how long each execution takes and if the execution takes longer than allowed it should "drop a frame".
my first approach would be
// simplified code
const fps = 40
const executionStats : number[] = []
executionStats.length = fps * 10
const startFrame = new Date()
type SingleValue = {current: number, effectType: string, effectStarted: number}
const values : SingleValue[] = [...] // up to 8192 entries
// this is calculating the target value based on some lookups in other objects
// and based on when the effect started and in what tick we are in right now
const runSingleEffect = (value: SingleValue, tick: number) : number => {...}
const handler = async (tick: number) => {
values.forEach(value => {value.current = runSingleEffect(value, tick)})
}
setInterval(async () => {
const start = new Date()
await handler((start - startFrame) / fps);
const end = new Date()
executionStats.push(end - start)
executionStats.unshift()
}, 1000 / fps)
is there any better approach than using a simple setInterval? (obviously I could use a ring buffer for the execution stats) but is there a better way to make sure that only one handler runs at a time? Is there maybe a library for that kind of stuff?
I would use setTimeout with continuous resubscription. You would recalculate the next tick on every "loop" end.
function defer (ms) {
const frameBudgetMs = 1000 / fps;
setTimeout(async () => {
const start = Date.now();
try {
await handler((start - startFrame) / fps);
} finally {
const end = Date.now();
const ms = end - start;
executionStats.push(ms);
executionStats.unshift();
// a) In case we are over our frame, start next defer immediately.
let nextTickMs = Math.max(0, frameBudgetMs - ms);
defer(nextTickMs);
// b) In case we want to execute at specific frame interval, but just dropping the frames
let nextTickMs = frameBudget - ms % frameBudget;
defer(nextTickMs);
}
}, ms)
}
// start our loop
defer(0);
As you can see, you are completely flexible in resubscription scenarios, and no parallel runs are possible in such a "loop".

Node-Cron: how to ensure last job is complete

I am using node-cron to send operational emails (currently searching for new emails that need to be sent every minute).
My function (operationalEmails) looks for mongodb records that have a sent flag = false, sends the email, then changes the sent flag = true.
If an iteration of the cron has a large payload of records to send, it could take more than a minute.
How do I ensure the last iteration of the cron is complete before starting a new one?
//Run every Min
cron.schedule("* * * * *", () => {
operationalEmails();
});
you would need to create a simple lock
const running = false;
function operationalEmails() {
if (running) {
return
}
running = true;
// do stuff
running = false;
}
//Run every Min
cron.schedule("* * * * *", () => {
operationalEmails();
});

How to prevent code from excecuting multiple times

This code ends up spamming messages for one second every time it runs. Does anyone know to prevent that? the variable count is the amount of messages sent in the last hour, and my goal is if more then 15 were sent in the past hour then to send a message. Here is the code I tried:
var count = await keyv.get('messagecount')
const sendtochan = await rotate.guild.channels.cache.get('760626595332358205');
cron.schedule('0 0 * * * *', async () => {
if (count > 14 && !rotate.author.bot) {
keyv.set('messagecount', 0)
const sendtochan = await rotate.guild.channels.cache.get('760626595332358205');
sendtochan.send(`This channel's current topic is: **${await keyv.get('topic')}**`);
return
}
if (count < 14 && !rotate.author.bot) {
keyv.set('messagecount', 0)
return
}
});

Node Red setInterval causing stale readings on redeploy

I'm tinkering around with Node-Red and have made a my first simple custom node which adds to a counter each time an input is received.
The node also contains a call to setInterval which after ever 5 seconds calls node.send() with the counter value in the msg.payload.
module.exports = function(RED) {
function MyNode(config) {
RED.nodes.createNode(this, config);
var node = this;
var count = 0;
this.on('input', function(msg) {
count++;
});
setInterval(function() {
var msg =
{
payload: { countTotal : count }
};
node.send(msg);
return;
}, 5000); // 5 Second Timer
}
RED.nodes.registerType("my-node", MyNode);
}
If I deploy the flow the node is behaving as expected. However if I redeploy the flow I seem to be receiving two calls from the node.send().
One which is the correct value and the other is the last/stale value that was current before the redeploy.
For example:
Deploy and click input 6 times before 5 seconds
05 seconds: countTotal = 6
10 seconds: countTotal = 6
15 seconds: countTotal = 6
Re-deploy and click input 3 times before 5 seconds
05 seconds: countTotal = 3
05 seconds: countTotal = 6 [Old Stale reading]
If I restart node-red then it starts behaving as I'd expect.
My questions
Is this the correct approach to having a timer within a node, if not what should I be doing? If yes, why is the stale data coming through?
Many thanks.
UPDATE
Off the back off the answer from knolleary I've tried the following change
module.exports = function(RED) {
function MyNode(config) {
RED.nodes.createNode(this, config);
var node = this;
var count = 0;
var timer = setInterval(function() {
var msg = { payload: { countTotal : count }};
node.send(msg);
return;
}, 5000); // 5 Second Timer
this.on('input', function(msg) {
count++;
});
this.on('close', function(msg) {
clearTimeout(timer);
});
}
RED.nodes.registerType("my-node", MyNode);
}
...however now when a the flow is re-deployed, it completely stops!!
You are creating a timer each time a node is created, but at no time are you clearing the timer when the node is 'closed'. That is why you are getting multiple timers firing each time you deploy the node.
You need to add a handler for the close event, and tidy up any resources the node is using or has created - such as the timer.
http://nodered.org/docs/creating-nodes/node-js#closing-the-node

Resources