ReactJS : How to detect network idle between XHR requests - javascript

I'm fairly new to the JS world and working with React JS. Couldn't find anything for the exact solution.
I want to implement an App level idle timer which would be activated when the user does not make any server request for a specific time period. The function should be triggered after X mins of the last XHR request irrespective of whether the user navigates between components or not.
For example - If user clicks on a button which calls an API and just plays around the screen without making another service call for X mins, the function should be triggered. At this stage, though user is not idle on the website, there's an idleness in the network activity. I want to detect this and perform some actions.
Also, would like to know the performance impact of the solution. Thanks!

Use PerformanceObserver to detect the fetch and then add/update timer using the setTimeout
let timer;
const observer = new PerformanceObserver((items) => {
items
.getEntries()
.filter(({ initiatorType }) => initiatorType === "fetch")
.forEach((entry) => {
console.log("Made fetch request", entry.name);
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => console.log("(idle) After 2 sec"), 2000);
});
});
observer.observe({
entryTypes: ["resource"],
});
fetch("https://swapi.dev/api/planets/1/").then((res) => res.json());
setTimeout(
() => fetch("https://swapi.dev/api/planets/2/").then((res) => res.json()),
1000
);
setTimeout(
() => fetch("https://swapi.dev/api/planets/3/").then((res) => res.json()),
3000
);

The Network Information Api.
I'm not sure but you can try subtract downlink from downlinkMax.
But this Api is not well supported Can i use

Related

Click A button faster

We are playing a clicker game on a website where a button will be displayed at a random time, first 7 players to click it will be shown as the winners of that round. I wrote the clicker function bellow and always use it on my Chrome console.
(function () {
setInterval(() => {
const button = document.querySelector(“.click-button);
if (!button) return;
button.click();
}, 5);
})();
I was playing it with mobile network of 50 - 70 mbps of speed and was among the top 5 until when other players started using VPS Machine which have over 4gbps of internet speed. I now have a VPS running Windows server 2022 but cant still perform better. So My question is, Is it that my VPS speed is slower than their own or My Laptop have less specification than their own or I need a better javascript code than the one above?
Running the above code on a VPS server with a download speed of 4.3gbps and upload speed of 4.1gbps through a browser console and click faster
Instead of polling for the button, you could use a MutationObserver that will notify you when the button changes or is inserted into the document. Unless you can get to the event that triggers the button activation, this is probably the fastest you can do.
Assuming that the button is inserted into a <div id="button-container"></div>:
const callback = mutations => {mutations.forEach(m => m.addedNodes.forEach(node => node.tagName === 'BUTTON' && node.click()))}
const observer = new MutationObserver(callback)
const container = document.getElementById('button-container')
observer.observe(container, {childList: true})
Have a look at this codepen
Replacing the forEach() with a for will give you a few additional nanoseconds.
Your code can be a little faster by caching the function and the button outside the interval
(function() {
const button = document.querySelector(".click-button");
const buttonClick = () => button.click();
if (button) setInterval(buttonClick, 5);
})();
If the button does not exist at all, then the code above will not work. Then you do need the mutation oberver

How to fetch timestamp based on the internet instead using user's device time in Javascript?

I tried to create a get timestamp function like Date.now(). I assume that Date.now() will use the time from the user's computer, so there is no guarantee that the UNIX time is accurate if the user sets the time manually. I would create a function to get a standardized timestamp from the time server API instead, to make sure that the timestamp is the same for all of the users.
function timefromInternet() {
return new Promise((resolve, reject) => {
fetch("http://worldtimeapi.org/api/timezone/Asia/Taipei")
.then(response => response.json())
.then(data => {
resolve(data.unixtime);
}).catch(error => { resolve(Date.now()); });
});
}
but it is too slow, so I could not execute like Date.now() for example like this:
let callInfo = {
timestamp: Date.now(),
status: "PAUSE",
};
this.$store.commit("setCallInfo", callInfo);
this.$store.commit("updateLocalUserByObject", {
status: callInfo.status,
});
I want to replace Date.now() with something like this:
let callInfo = {
timestamp: timefromInternet(),
status: "PAUSE",
};
this.$store.commit("setCallInfo", callInfo);
this.$store.commit("updateLocalUserByObject", {
status: callInfo.status,
});
What is the best solution to modify timefromInternet() so it could be run like Date.now()? Because if I am using promises, I could not call like Date.now() above. Thanks in advance.
You can request the time only once at page load and then make your getTimeStamp() synchronous.
There are a few clock timers available in the browser, some that the users can't easily mess with. For instance performance.now() will be relative to the page load and will use a monotonic clock which should keep ticking, no matter what the user does with its System Settings (contrarily to Date, which in some UAs always request the system date, not only at page load).
One case where this clock may stop ticking is when the computer goes to sleep. One workaround for that is to make a new request when the page gains focus again.
async function fetchExactDateFromServer() {
const resp = await fetch("https://worldtimeapi.org/api/timezone/Europe/Paris");
if (!resp.ok) {
throw new Error("Network Error");
}
return +new Date((await resp.json()).datetime);
}
let offset = 0;
function getTimeStamp() {
return Math.round(performance.now() - offset);
}
function updateTime() {
return fetchExactDateFromServer()
.then((officialTime) => {
offset = performance.now() - officialTime;
})
.catch((err) => {
console.error(err);
return Promise.reject(err);
});
}
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === 'visible') {
updateTime();
}
});
// test by logging the difference between server-side and local (should always be about the same difference)
const btn = document.querySelector("button");
updateTime().then(() => {
btn.onclick = (evt) => console.log(getTimeStamp(), Date.now(), getTimeStamp() - Date.now());
btn.disabled = false;
});
<button disabled>Test</button>
I think that after reading your comments (in regard to protecting against free trial abuses) you're approaching this in logically the wrong way.
Even with a super-efficient way of pulling an accurate timestamp from the Internet in some way, it is actually trivial for anyone to simply replace the function/return locally in their browser, and so defeat your test. They don’t even need to supply a timestamp, they can just replace the function with something that returns TRUE for every test. Why? Because anything you test in the client (browser) is open to modification by a reasonably sophisticated user, which means that all validation must be enforced server-side if it is to avoid being circumvented.
As an alternative, simply store the trial period details at the server end (where you have total control over the validity of the timestamp), and if a request from the user is made after the trial has expired, then reject it.
You should trust the system clock. Virtually all modern OSen are running an NTP daemon of some flavour or other.
There's too much stuff these days, Kerberos off the top of my head, and SSL/TLS, that depends on clock synchronicity for them to do otherwise.
About the only exception might be systems in a hardened, air-gapped, environment. And they've probably got an accurate time source of their own (GPS receiver, atomic clock radio, etc.)

Manage timer between windows, browser tabs. localStorage events

I was searching for a way how to communicate between multiple tabs or windows in a browser (on the same domain, not CORS).
I am using timer and it should working on all site pages. If there's a way to stop/pause/play timer on all pages (new windows) simultaneously ?
For example I have 2 tabs - page 1 and page 2... on both pages I have running the same timer. If I click pause timer on page 1 it must pause on page 2 too.
I can't find a solution all day long ... help please.
My timer code:
const Timer = easytimer.Timer;
const getSavedTime = () => JSON.parse(localStorage.getItem('time')) || {};
const instance = new Timer({ startValues: getSavedTime() });
instance.addEventListener('secondsUpdated', () => {
document.querySelector('.example').textContent = instance.getTimeValues().toString();
localStorage.setItem('time', JSON.stringify(instance.getTimeValues()));
});
instance.addEventListener('started', () => localStorage.setItem('running', '1'));
instance.addEventListener('paused', () => localStorage.removeItem('running'));
instance.addEventListener('stopped', () => {
localStorage.removeItem('time');
localStorage.removeItem('running');
document.querySelector('.saved').textContent = '';
document.querySelector('.example').textContent = '';
});
document.querySelector('.saved').textContent = localStorage.getItem('time');
document.querySelector('.example').textContent = instance.getTimeValues().toString();
document.querySelector('.start-button').addEventListener('click', () => instance.start({ startValues: getSavedTime() }));
document.querySelector('.pause-button').addEventListener('click', () => instance.pause());
document.querySelector('.stop-button').addEventListener('click', () => instance.stop());
if (localStorage.getItem('running') === '1') {
instance.start({ startValues: getSavedTime() });
}
listen event:
window.addEventListener('storage', function (e) {
console.log("storage event occured here");
},false);
You can use BroadcastChannel to communicate between different tabs within the same origin.
The harder part would be managing which tab gets to "run" the timer code and which tabs just "listen" to the events. Then, if the tab that was running the timer got closed, how would you coordinate deciding which other tab should take over that role.
A better option might be to put the timer code in a Web Worker (specifically a SharedWorker), and then just use BroadcastChannel (and/or maybe the port property of the SharedWorker) to send the events out to the tabs.
If you're lucky, you might be able to get your easytimer.Timer class to run within a Web Worker without any modifications - although you would still need to do some work to hook it up to the BroadcastChannel.

How to update this in real time rather than reloading browser again and again?

Am learning xhr, how to keep price loading in real time on browser? When we send GET request to the api, we receive a json response which has the price, I don't want to reload browser tab again and again to check price, I want it to update in real time, how to do?
<!DOCTYPE html>
<html>
<head>
<title>TEST</title>
</head>
<body>
<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.onload = function(){
document.write(this.responseText)
};
xhr.open("GET","https://api.coindesk.com/v1/bpi/currentprice/USD.json",true);
xhr.send();
</script>
</body>
</html>
What you're asking is basically how to schedule your code to run in the future. The built-in mechanisms for that are setTimeout(), which runs things once, and setInterval(), which runs them multiple times. For example, you could:
setInterval(function () {
var xhr = new XMLHttpRequest();
xhr.onload = function(){
document.write(this.responseText)
};
xhr.open("GET","https://api.coindesk.com/v1/bpi/currentprice/USD.json",true);
xhr.send();
}, 10 * 1000);
This runs your code every 10 seconds. (10 multiplied by 1000 milliseconds.) There's a problem though, in that your GET request may take longer than 10 seconds to finish. This is particularly true on bad mobile connections and high latency links, such as those of satellite users. To solve that problem, you need to use setTimeout() instead, and trigger your code to run once the first request is finished. You should be sure to also include the error case, as you don't want your loop to stop if there's just one error. To make all this simpler, I'll switch to using the Fetch API. (Fetch is what you should be using these days. It's more robust than XHR, and is well-supported by browsers.)
function updatePrices() {
return fetch('https://api.coindesk.com/v1/bpi/currentprice/USD.json').then({
if (res.ok) {
throw new Error('Request failed');
}
return res.json()
}).then((data) => {
console.log(data);
setTimeout(updatePrices, 10 * 1000);
}).catch((e) => {
setTimeout(updatePrices, 5 * 1000); // If fail, try again sooner
});
}
updatePrices();
Now, you have updates every 10 seconds. But, you asked for realtime. For that, you need a different tool.. server-sent events.
If you are in control of the server, you can choose to support this simple text-based protocol. This allows the server to push data to you immediately when its updated. Setting up the event source on the client is very simple:
const eventSource = new EventSource('https://example.com/bitcoin-prices');
eventSource.addEventListener('message', (e) => {
console.log(e.data);
});
The EventSource will even reconnect if a connection is lost.
I hope this helps you get started!

Set Timeout not working in React Native - Debug JS Remotely

I drove myself nuts trying to figure out what the problem is with my code. I am building a React Native application and developing in the IOS Simulator after running run-ios. I enabled Debug JS Remotely so I can verify the condition of my tokens returned from the server.
I am attempting to build a simple service that will refresh my tokens using a setTimeout.
let sessionTimeout = null;
export const session = (response) => {
let tokens = response.tokens;
Store.set(tokens);
setSessionTimeout(tokens.access.expiresIn);
};
const setSessionTimeout = (duration) => {
let time = duration - 5000;
clearTimeout(sessionTimeout);
sessionTimeout = setTimeout(() => {
console.log('settimeout called');
refreshToken();
}, 1000);
};
const refreshToken = () => {
let tokens = Store.get();
refresh(tokens)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
};
In the above code a began my initial test by hardcoding 1000 and it worked fine. It successfully made the call to my server. I then attempted to implement the proper time duration and found it does not work. Even by replacing the 1000 with 2000 causes it to fail and the call to the backend is never made.
I then tried using the following packages and got the same result:
react-native-timer
react-timer-mixin
After banging my head on the wall for two days and having another attempt on stack shot down because my issue could not be reproduced I disabled the Debug JS Remotely and everything works as expected.
However now I am wondering how I am supposed to debug the frontend portion of my application with out the use of this feature. If anyone has any ideas or advice on issues like this I would appreciate the feedback as this is my first React Native app.

Categories

Resources