Update data every 60 sec - vanilla js SPA - javascript

I have this function that renders some html, and I don't know how to call setInterval function here, to call render func after every 60 sec
const Home = {
render: async () => {
const cryptos = await getAllCryptos();
const view = `
<section class="section">
<table>
${cryptos.data.map(crypto =>
`<tr>
<td class="name">${crypto.name} </td>
<td>${crypto.symbol}</td>
<td>${crypto.quote.USD.price}</td>
<td>${crypto.quote.USD.percent_change_24h}</td>
</tr>`
)}
</table>
</section>
`;
return view
}
};
export default Home;
I cant really put render function inside setInterval, so I am wondering what is the best way to do it?

Indeed, using setInterval would be chaotic given that render involves asynchronous processing.
Instead, a chained series of setTimeout would probably be best:
const RENDER_INTERVAL = 60000; // 60 seconds in milliseconds
function handleRender() {
Home.render()
.then(html => {
// ...use the HTML...
})
.catch(error => {
// ...report the error...
})
.finally(scheduleRender);
}
function scheduledRender() {
setTimeout(handleRender, RENDER_INTERVAL);
}
That code assumes you want to continue even if one call to Home.render fails.
If you want to use 60 seconds from the beginning of the last call to render rather than the end (the above is 60 seconds from the end), you'd do a bit more logic:
const RENDER_INTERVAL = 60000; // 60 seconds in milliseconds
let lastRenderStart = 0;
function handleRender() {
lastRenderStart = Date.now();
Home.render()
.then(html => {
// ...use the HTML...
})
.catch(error => {
// ...report the error...
})
.finally(scheduleRender);
}
function scheduledRender() {
setTimeout(handleRender, Math.max(0, RENDER_INTERVAL - (Date.now() - lastRenderStart));
}
handleRender();

Related

JavaScript clear timeout not working with React useEffect return function

For some reason, I need to call checkProgess function for the first 2 minutes with 10 seconds of delays.
So I did it like this
const [timers, setTimers] = useState<ReturnType<typeof setTimeout>[]>([]);
useEffect(()=>{
for (let index = 0; index < 12; index++) {
const seconds = (index+1)*10000;
let timer = setTimeout(() => {
checkProgress(id, setFieldValue, contractType, fileOCRStatus);
}, seconds);
console.log("timer",timer)
setTimers((prev) => {
prev.push(timer)
return prev
});
}
},[])
within these 12 tries, this component will unmount if the progress checks succeed. In that time I do want to clear all the remaining timeout calls. I did it like this in the useEffect return function.
return () => {
console.log("Return function called",timers)
timers.forEach((timer) => clearTimeout(timer));
};
This part is executed successfully but the cleaning thing seems not working. I CAN SEE THE API CALLS RUNNING AFTER THE COMPONENT IS UNMOUNTED.
what went wrong here?
In console.log("Return function called", timers) timer ids also print correctly.
You don't actually need to store these in a state since this useEffect is only running once (and even if it wasn't, since you're cleaning up the timers in the returned function, you don't need to keep the value across renders).
// delete the useState stuff
useEffect(() => {
const timers = [];
for (let index = 0; index < 12; index++) {
const seconds = (index+1)*10000;
const timer = setTimeout(() => {
checkProgress(id, setFieldValue, contractType, fileOCRStatus);
}, seconds);
console.log("timer",timer)
timers.push(timer);
}
return () => {
timers.forEach((timer) => clearTimeout(timer));
}
}, []);

Call an API every 2 seconds for a period of one minute using react hooks

I am trying to make an api call (API1) every 2 seconds for a period of 1 minute. During this minute I need to exit the interval if condition is met and do another API call (API2). The problem that I am facing is if the API1 get into a pending state for more than 2 seconds, the second call of API1 will start and again if in pending state for 2 seconds the third call will happen ending up with quite few calls for same api all under pending state …. How can I stop the subsequent calls until the previous call is resolved?
useEffect(() => {
if (triggerTheInterval) {
startInterval()
return () => clearInterval(id)
}
}, [triggerTheInterval])
onClick = () => {
makefirstcallforapi1();
}
//this is only to get the first call at first second - before the 2 seconds interval starts
const makefirstcallforapi1 = () => {
setTimer(new Date().getTime());
fetch(url).then(res => {
if (conditionmet) {
//do the API2 call
} else {
setTriggerTheInterval(true)
}
})
startInterval = () => {
id = setInterval(() => {
fetch(url).then(res => {
if ((new Date().getTime() - startTime > 60000 || condtionmet) {
//do something then exit the interval
}
})
}, 2000)
}

using setInterval and setimeout to make 3 components load after each other

I am making a simple react app, it just loads one component when it reached 100% then it loads the second one and after its 100% it loads the third one.
this is how it works, I have a start button, when you click it, it starts the first function which loads the first component:
<Button
variant="outlined"
type="submit"
onClick={() => {
setLoading(true);
start();
}}
>
Start
</Button>
and here are the loading components they are all the same expect the state is different:
const load1 = () => {
const timer = setInterval(() => {
setProgress1((prevProgress) =>
prevProgress >= 100 ? 100 : prevProgress + 10
);
}, 800);
return () => {
clearInterval(timer);
};
};
const load2 = () => {
const timer = setInterval(() => {
setProgress2((prevProgress) =>
prevProgress >= 100 ? 100 : prevProgress + 10
);
}, 800);
return () => {
clearInterval(timer);
};
};
const load3 = () => {
const timer = setInterval(() => {
setProgress3((prevProgress) =>
prevProgress >= 100 ? 100 : prevProgress + 10
);
}, 800);
return () => {
clearInterval(timer);
};
};
and my start looks like this:
const analyseDNA = () => {
setTimeout(load1, 2000);
setTimeout(setLoading2(true), 2000);
setTimeout(load2, 4000);
setTimeout(setLoading3(true), 4000);
setTimeout(load3, 6000);
};
what is supposed to happen is to load component 1 and then component 2 and then component 3 after each other. then you can press start again and it does the same.
however, with this code, component 1 and component 2 are loaded together, and then after 6 seconds component 3, when you press start again it only loads component 1, and component 3 is already loaded and nothing happens with component 2. when I open console I can see that it is printing 1, 2, 3, and it seems like the interval is never ends and it keeps going , how can I fix this?
I've made you a code sandbox. Could be prettier but it should get you on the right path. The trick would be to pass in a callback function to the load function. When the loader reaches 100 from the interval, invoke that callback which calls the next loader.
Personally, I think promises are more suitable. You would do something along the lines of creating a set interval inside a promise, then resolve the promise when the value reaches 100. This way you could make your analyseDNA function more like this:
const analyseDNA = async () => {
await load1()
await load2()
await load3()
};
Here is a sandbox for a "promise" approach. Of course, you might need to adapt if you want to show progress.

Set Timeout running 4 times each minute

for some reason a function I would like to execute every minute is running 4 times every minute. I would like the behavior to only fire a single time. I am unsure of why it is firing multiple times. the code is as follows:
const checkToken = () => {
console.log('im running')
const token = localStorage.FBIdToken
if (token && token !== 'Bearer undefined') {
const decodedToken = jwtDecode(token)
if (decodedToken.exp * 1000 < Date.now()) {
localStorage.removeItem('FBIdToken')
window.location.reload()
}
}
setTimeout(checkToken, 60 * 1000)
}
checkToken()
you are using this script in react, so make sure that this function/method is not triggering with component re-rendering. if you are using stateful component then move this function to componentDidMount to stop the multiple calls to this method. and if you are using stateless component then use hooks to avoid this issue
you can also use the clearInterval to avoid this issue
const timeInterVal = null;
const checkTokenFunc = () => {
if (token && token !== 'Bearer undefined') {
const decodedToken = jwtDecode(token)
if (decodedToken.exp * 1000 < Date.now()) {
localStorage.removeItem('FBIdToken')
window.location.reload()
}
}
}
const checkToken = () => {
if(timeInterval!==null){
clearTimeout(timeInterval);
}
timeInterval = setTimeout(() => {
checkTokenFunc();
checkToken();
}, 60 * 1000)
}
checkToken();
You could use setInterval() instead of setTimeout(). setInterval() you can specify a period of time and it will keep running based on the time interval you set...Pass setInterval() a function to execute and a time interval in milliseconds. The below will execute every 5 seconds.
Example:
setInterval(function(){ alert("Hello"); }, 5000);

How to calculate the execution time of an async function in JavaScript?

I would like to calculate how long an async function (async/await) is taking in JavaScript.
One could do:
const asyncFunc = async function () {};
const before = Date.now();
asyncFunc().then(() => {
const after = Date.now();
console.log(after - before);
});
However, this does not work, because promises callbacks are run in a new microtask. I.e. between the end of asyncFunc() and the beginning of then(() => {}), any already queued microtask will be fired first, and their execution time will be taken into account.
E.g.:
const asyncFunc = async function () {};
const slowSyncFunc = function () {
for (let i = 1; i < 10 ** 9; i++) {}
};
process.nextTick(slowSyncFunc);
const before = Date.now();
asyncFunc().then(() => {
const after = Date.now();
console.log(after - before);
});
This prints 1739 on my machine, i.e. almost 2 seconds, because it waits for slowSyncFunc() to complete, which is wrong.
Note that I do not want to modify the body of asyncFunc, as I need to instrument many async functions without the burden of modifying each of them. Otherwise I could just add a Date.now() statement at the beginning and the end of asyncFunc.
Note also that the problem is not about how the performance counter is being retrieved. Using Date.now(), console.time(), process.hrtime() (Node.js only) or performance (browser only) will not change the base of this problem. The problem is about the fact that promise callbacks are run in a new microtask. If you add statements like setTimeout or process.nextTick to the original example, you are modifying the problem.
Any already queued microtask will be fired first, and their execution time will be taken into account.
Yes, and there's no way around that. If you don't want to have other tasks contribute to your measurement, don't queue any. That's the only solution.
This is not a problem of promises (or async functions) or of the microtask queue specifically, it's a problem shared by all asynchronous things which run callbacks on a task queue.
The problem we have
process.nextTick(() => {/* hang 100ms */})
const asyncFunc = async () => {/* hang 10ms */}
const t0 = /* timestamp */
asyncFunc().then(() => {
const t1 = /* timestamp */
const timeUsed = t1 - t0 /* 110ms because of nextTick */
/* WANTED: timeUsed = 10ms */
})
A solution (idea)
const AH = require('async_hooks')
const hook = /* AH.createHook for
1. Find async scopes that asycnFunc involves ... SCOPES
(by handling 'init' hook)
2. Record time spending on these SCOPES ... RECORDS
(by handling 'before' & 'after' hook) */
hook.enable()
asyncFunc().then(() => {
hook.disable()
const timeUsed = /* process RECORDS */
})
But this wont capture the very first sync operation; i.e. Suppose asyncFunc as below, $1$ wont add to SCOPES(as it is sync op, async_hooks wont init new async scope) and then never add time record to RECORDS
hook.enable()
/* A */
(async function asyncFunc () { /* B */
/* hang 10ms; usually for init contants etc ... $1$ */
/* from async_hooks POV, scope A === scope B) */
await /* async scope */
}).then(..)
To record those sync ops, a simple solution is to force them to run in a new ascyn scope, by wrapping into a setTimeout. This extra stuff does take time to run, ignore it because the value is very small
hook.enable()
/* force async_hook to 'init' new async scope */
setTimeout(() => {
const t0 = /* timestamp */
asyncFunc()
.then(()=>{hook.disable()})
.then(()=>{
const timeUsed = /* process RECORDS */
})
const t1 = /* timestamp */
t1 - t0 /* ~0; note that 2 `then` callbacks will not run for now */
}, 1)
Note that the solution is to 'measure time spent on sync ops which the async function involves', the async ops e.g. timeout idle will not count, e.g.
async () => {
/* hang 10ms; count*/
await new Promise(resolve => {
setTimeout(() => {
/* hang 10ms; count */
resolve()
}, 800/* NOT count*/)
}
/* hang 10ms; count*/
}
// measurement takes 800ms to run
// timeUsed for asynFunc is 30ms
Last, I think it maybe possible to measure async function in a way that includes both sync & async ops(e.g. 800ms can be determined) because async_hooks does provide detail of scheduling, e.g. setTimeout(f, ms), async_hooks will init an async scope of "Timeout" type, the scheduling detail, ms, can be found in resource._idleTimeout at init(,,,resource) hook
Demo(tested on nodejs v8.4.0)
// measure.js
const { writeSync } = require('fs')
const { createHook } = require('async_hooks')
class Stack {
constructor() {
this._array = []
}
push(x) { return this._array.push(x) }
peek() { return this._array[this._array.length - 1] }
pop() { return this._array.pop() }
get is_not_empty() { return this._array.length > 0 }
}
class Timer {
constructor() {
this._records = new Map/* of {start:number, end:number} */
}
starts(scope) {
const detail =
this._records.set(scope, {
start: this.timestamp(),
end: -1,
})
}
ends(scope) {
this._records.get(scope).end = this.timestamp()
}
timestamp() {
return Date.now()
}
timediff(t0, t1) {
return Math.abs(t0 - t1)
}
report(scopes, detail) {
let tSyncOnly = 0
let tSyncAsync = 0
for (const [scope, { start, end }] of this._records)
if (scopes.has(scope))
if (~end) {
tSyncOnly += end - start
tSyncAsync += end - start
const { type, offset } = detail.get(scope)
if (type === "Timeout")
tSyncAsync += offset
writeSync(1, `async scope ${scope} \t... ${end - start}ms \n`)
}
return { tSyncOnly, tSyncAsync }
}
}
async function measure(asyncFn) {
const stack = new Stack
const scopes = new Set
const timer = new Timer
const detail = new Map
const hook = createHook({
init(scope, type, parent, resource) {
if (type === 'TIMERWRAP') return
scopes.add(scope)
detail.set(scope, {
type: type,
offset: type === 'Timeout' ? resource._idleTimeout : 0
})
},
before(scope) {
if (stack.is_not_empty) timer.ends(stack.peek())
stack.push(scope)
timer.starts(scope)
},
after() {
timer.ends(stack.pop())
}
})
// Force to create a new async scope by wrapping asyncFn in setTimeout,
// st sync part of asyncFn() is a async op from async_hooks POV.
// The extra async scope also take time to run which should not be count
return await new Promise(r => {
hook.enable()
setTimeout(() => {
asyncFn()
.then(() => hook.disable())
.then(() => r(timer.report(scopes, detail)))
.catch(console.error)
}, 1)
})
}
Test
// arrange
const hang = (ms) => {
const t0 = Date.now()
while (Date.now() - t0 < ms) { }
}
const asyncFunc = async () => {
hang(16) // 16
try {
await new Promise(r => {
hang(16) // 16
setTimeout(() => {
hang(16) // 16
r()
}, 100) // 100
})
hang(16) // 16
} catch (e) { }
hang(16) // 16
}
// act
process.nextTick(() => hang(100)) // 100
measure(asyncFunc).then(report => {
// inspect
const { tSyncOnly, tSyncAsync } = report
console.log(`
∑ Sync Ops = ${tSyncOnly}ms \t (expected=${16 * 5})
∑ Sync&Async Ops = ${tSyncAsync}ms \t (expected=${16 * 5 + 100})
`)
}).catch(e => {
console.error(e)
})
Result
async scope 3 ... 38ms
async scope 14 ... 16ms
async scope 24 ... 0ms
async scope 17 ... 32ms
∑ Sync Ops = 86ms (expected=80)
∑ Sync&Async Ops = 187ms (expected=180)
Consider using perfrmance.now() API
var time_0 = performance.now();
function();
var time_1 = performance.now();
console.log("Call to function took " + (time_1 - time_0) + " milliseconds.")
As performance.now() is the bare-bones version of console.time , it provide more accurate timings.
you can use console.time('nameit') and console.timeEnd('nameit') check the example below.
console.time('init')
const asyncFunc = async function () {
};
const slowSyncFunc = function () {
for (let i = 1; i < 10 ** 9; i++) {}
};
// let's slow down a bit.
slowSyncFunc()
console.time('async')
asyncFunc().then((data) => {
console.timeEnd('async')
});
console.timeEnd('init')

Categories

Resources