How to solve the invalid problem of electron's setInterval? - javascript

I encountered a difficult problem when trying to write an electron application. The following is a detailed description of the problem:
I plan to load a clock on the page of the electron rendering process. Just like our system time, it will refresh every second, 60 seconds is one minute, and so on, but I don’t use the system time, I use a current Time API, the json data returned by this API interface is the current time. I wrote a set of asynchronous execution functions in the main process main.js to get the data passed by this API. The following is the main process main.js Asynchronous get time API code:
const request = net.request('http://api.k780.com:88/?app=life.time&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json')
request.on('response', (response) => {
console.log(response.statusCode);
console.log(response.headers);
response.on('data', (chunk) => {
let result = JSON.parse(chunk).result;
let datetime = result.datetime_1;
let week = result.week_4;
console.log(datetime, week)
mainWindow.webContents.send('datetime', { datetime, week });
})
response.on('end', () => {
console.log('end');
})
})
request.end();
The console information displayed by the main process is as follows:
200
{
server: 'nginx',
date: 'Thu, 06 May 2021 01:38:00 GMT',
'content-type': 'application/json; charset=utf-8;',
'transfer-encoding': 'chunked',
connection: 'keep-alive',
'access-control-allow-origin': '*'
}
2021-05-06 09:38:01 Thursday
end
Then after requesting this API, it will respond with a timestamp of the current time. After the main process obtains this timestamp, it will be sent to my rendering process through webContents.send. The following is the code sent by the main process to the rendering process:
mainWindow.webContents.send('datetime', { datetime, week });
The rendering process obtains the time data sent by the main process through ipcRenderer.on, and then formats this timestamp and outputs it to my rendering process page. I wrote this set of code as a function as follows:
function getNetTime() {
//Get the date and time passed by the main process
ipcRenderer.on('datetime', (event, arg) => {
let datetime = arg.datetime; //Get date and time
let week = arg.week; //Get the day of the week
let year = datetime.substring(0, 4); //Get year
let month = datetime.substring(5, 7); //Get month
let day = datetime.substring(8, 10); //Get the day
let hour = datetime.substring(10, 13); //Get hour
let min = datetime.substring(14, 16); //Get minutes
let sec = datetime.substring(17, 19); //Get seconds
let weekday = ""; //Get Chinese weekday
const timeText = document.querySelector('#timeText')
// console.log(datetime);
// console.log(week);
// console.log(year,month,day,hour,min);
switch (week) {
case'Monday':
weekday ='Monday';
break;
case'Tuesday':
weekday ='Tuesday';
break;
case'Wednesday':
weekday ='Wednesday';
break;
case'Thursday':
weekday ='Thursday';
break;
case'Friday':
weekday ='Friday';
break;
case'Saturday':
weekday ='Saturday';
break;
case'Sunday':
weekday ='Sunday';
break;
}
timeText.innerHTML =`${year}year${month}month${day}day ${weekday}${hour}:${min}:${sec}`;
});
The problem now encountered is that although the current time can be displayed normally on the page of the rendering process, it cannot be automatically refreshed. I want to set it to refresh every 1000 milliseconds through setTimeout or setInterval, which is equivalent to one step in 1 second of the clock. But it has no effect. The current time can only be displayed when the program is reopened, and it cannot be automatically refreshed. The following is the code of setInterval:
window.onload = () => {
getNetTime();
setInterval(getNetTime(),1000)
}
The above is the problem I am encountering now. Electron is also just getting in touch. I am searching for a long time on net. But no use. Please help or try to give some ideas how to achieve this.

The problem you're experiencing has nothing to do with Electron or how you request the data from the API. It's about how you set your interval.
The setInterval() function requires a function or a string of code as a parameter. However, you are specifying getNetTime() as the code it should call. This is not a function. It is a function call and will be evaluated before setInterval() is called. This will not do anything because the return type of getNetTime() is undefined.
To mitigate this, you can use a function, an arrow function or just leave out the parentheses. Whatever you choose is up to your liking.
setInterval (getNetTime, 1000);
// or
setInterval (() => getNetTime (), 1000);
// or
setInterval (function () { getNetTime (); }, 1000);

Related

I would like to better understand this code to save in cache with expiration date

Guys I have this code that saves my products in cache, I would like to better understand how it works,
getItemById(id) {
// return this.http.get(`${environment.API_URL}item/getById/${id}`);
return
this.cacheS.getOrSetCache(`StoreService_getItemById_${id}_${this.layout.emp.id}`,
this.http.get(`${environment.API_URL}item/getById/${id}`), 300000);
}
getOrSetCache(key: string, request: Observable<any>, msToExpire = 300000): Observable<any> {
let cache: any = {};
cache = JSON.parse(localStorage.getItem(key));
return (cache?.data && (cache?.exp > Date.now())) ?
of(cache.data) :
request.pipe(tap(v => {
localStorage.setItem(key, JSON.stringify({data: v, exp: (Date.now() + msToExpire)}));
}));
}
My question is more about how it manages to save in cache with an expiration date, and when the date expires, make the request again
This code works, but I couldn't understand how, is there any other better way to cache with expiration date?
when you call getOrSetCache, you pass msToExpire to arguments. Date.now() returns the current time. If this element is not in the cache, you make a request and after receive data, you save in to ls, and set time to expire as date.now() + msToExpire. And in the next call of this function you compare exp from your save element with current time Date.now().
For example the cache is empty, and you call getOrSetCache at 17:00. And let's imagine that the msToExpire = 30 min. Since the cache is empty, we make a request and save the data to the cache exp: (Date.now() + msToExpire) = 17:00 + 30 min = 17:30. Now, if call your function at 17:20 o'clock, this condition cache?.data && (cache?.exp > Date.now())) returns true, cause 17:30 > 17:20 and you will receive data from cache. But if you call it at 17:31, condition will return false, and it make new request and rewrite data in cache with new timestamp

Limit messages to every 500 ms

I have some websocket that sends around 100's of data per second,I want to limit it to only 1 data per 500 ms.
onMessage(data) {
console.log(data); // This prints around 100 different times within 1 second
}
I tried something like below , Is this the right approach or is there any other better way to do it ? because this code runs 100 times per second.
var lastlog = new Date().getTime();
onMessage(data) {
currenttime = new Date().getTime();
if ( currenttime - lastlog > 500) {
console.log(data);
lastlog = new Date().getTime();
}
}
P.s : I can ignore remaining data and will be able to reduce the 500 ms to 200ms.. that is 5 data per second.
Here is another way of doing it, using the npm package throttle-debounce. This method is not "better". It can result is less code typed but you might not want the dependency on the package.
You can use the throttle function and specify how many milliseconds until it can be called again. Setting the second argument to true prevents the last request from being deffered -https://www.npmjs.com/package/throttle-debounce#notrailing.
The example below uses the library to throttle how often a button is pressed.
const { throttle } = throttleDebounce
const handleRequest = throttle(500, true, () => {
console.log('this request will be handled')
})
<script src='https://cdn.jsdelivr.net/npm/throttle-debounce#3.0.1/umd/index.js'></script>
<button onClick="handleRequest()">Mimick sending message</button>
Your use case might look like this:
import { throttle } from 'throttle-debounce'
const onMessage = throttle(500, true, () => {
console.log(data);
})
Less lines than your example, but that doesn't mean it's "better".

Double interval call and it should not be in node.js

I have problem on server side (node.js) with setInterval.
I want to check something every day at specific time and I set interval like this:
let maintainTime = backFunc.getNextMaintainTime(new Date());
let maintain = setInterval(() => {
let currentTime = new Date();
if (currentTime.getTime() > maintainTime.getTime()) {
maintainTime = backFunc.getNextMaintainTime(maintainTime);
//do the maintain
}
}, 360000);
//360000 to check on every hour
and here is my getNextMaintainTime function:
getNextMaintainTime: (maintainTime) => {
maintainTime.setDate(maintainTime.getDate() + 1);
maintainTime.setHours(4);
maintainTime.setMinutes(0);
maintainTime.setSeconds(0);
maintainTime.setMilliseconds(0);
return maintainTime;
}
When I test it out it works perfectly but when I start server on production every time it calls this function 2 times instead of 1 time at 4am, what could cause the problem?

Delete Post after a month

Ok, I have a useInterval (custom hook) that will delete a document from firestore after a given time is milliseconds. It works fine if I set it to 10 seconds, 1 minute.
However, when I set it to delete the document a month after it was created, it looks like it creates the document and gets deleted right away.
How can I set my interval to delete the document after a month from when it was created?
const docRef = firestore.doc(`posts/${id}`)
const deleDoc = () => docRef.delete();
//******************************************************************* */
//? Deleting post after a month(time managed in milliseconds)
const now = createdAt.seconds * 1000;
const monthFromNow = 2628000000;
const then = now + monthFromNow;
const timeLeft = then - Date.now();
//?custom hook
useInterval(() => {
docRef.delete();
}, timeLeft);
You can create a task which hits one of your cloud function endpoint using Cloud Task. You can set it to hit the API every 1 min.
In the API you can pull up all the documents which are older than 1 month and delete them using the above logic.

Wait for something to stop to trigger function

I'm using a date picker I created with the sly tool darsa.in and everything is perfect, except that if the user changes the days too fast JavaScript does not trigger the correct date for the function.
Is there a way for doing:
if (datepicker not active for x seconds)
Or is there a way to create a variable and trigger the function only if that variable does not change during x time? I need to give some time to JS so it does not trigger the function until the user is on the date he targets.
Some code follows below.
When the day of the day picker changes, I call loadDateMatches() which loads all the matches into the HTML. But if you change, for example, very quickly between day 1 and day 5, it may stop loading the matches on day number 3.
I'm looking for a way to not trigger the function loadDateMatches() until there has been some time without changing the date.
days.on('active', function (eventName) {
activeDate= this.rel.activeItem;
var logDate = new Date(d.getFullYear(), 0, activeDate + first + 1);
var startTime = new Date(logDate.getFullYear(), logDate.getMonth(), logDate.getDate(), 0, 0, 0);
DayBarConditions.startTime = startTime.getTime()/1000;
var endTime = new Date(logDate.getFullYear(), logDate.getMonth(), logDate.getDate(), 23, 59, 59);
DayBarConditions.endTime = endTime.getTime()/1000;
if (typeof loadDateMatches == 'function') {
loadDateMatches();
}
});
Try having the date picker call a function on a delay that first checks whether the set day is the same as when it was changed, then loads the info if so. I believe the below code should be functional, however it is untested.
days.on('active', function (eventName) {
activeDate= this.rel.activeItem;
// We have to put this in a separate function, so that it evaluates activeDate
// when the date picker is changed, not when activateDelayed is called
(function(activeDate) {
//Activate the function after .5 seconds if date remains unchanged
window.setTimeout(activateDelayed, 500, activeDate);
})(activeDate);
};
function activateDelayed (oldDate) {
activeDate = days.rel.activeItem;
if (oldDate == activeDate) {
var logDate = new Date(d.getFullYear(), 0, activeDate + first + 1);
var startTime = new Date(logDate.getFullYear(), logDate.getMonth(), logDate.getDate(), 0, 0, 0);
DayBarConditions.startTime = startTime.getTime()/1000;
var endTime = new Date(logDate.getFullYear(), logDate.getMonth(), logDate.getDate(), 23, 59, 59);
DayBarConditions.endTime = endTime.getTime()/1000;
if (typeof loadDateMatches == 'function') {
loadDateMatches();
}
}
});
You could use this code, which keeps track of the number of requests you have for executing loadDateMatches. When it is the first one, the function is executed immediately, but the request counter is not decreased until also the cool-down period has passed. Only then is the counter decreased. While that counter is 1, another request can be added, but it will only lead to an execution when the first cool-down period has expired. Any more requests during that cool-down period will not change anything -- at the most one request will be pending for execution after the cool-down:
var requests = 0;
days.on('active', function (eventName) {
// ... your existing code, setting DayBarConditions properties, comes here.
// ...
if (typeof loadDateMatches == 'function') {
// Keep track of number of requests
if (requests < 2) requests++;
// Ignore this when there is currently a cool-down ongoing, and
// another execution is already pending:
if (requests == 2) return;
(function loop() {
loadDateMatches();
setTimeout(function () {
// Cool down has passed: repeat when new request is pending
if (--requests) loop();
}, 500);
})();
}
});
So, this code will not delay the very first request, but introduce a cool-down period during which any further requests are joined into one, and which will only execute when that cool-down period has expired.
But there may be better solutions depending on which code you have running in loadDateMatches.

Categories