I am trying to return a function that only invokes a callback function 'func' once per every 'wait' milliseconds.
Additional calls to the callback 'func' within the 'wait' period should NOT be invoked or queued.
This is what I have so far...
function throttle(func, wait) {
function inner(...args) {
setInterval(func(...args), wait);
}
return inner;
}
When I run the code through the test algorithm I get the following errors:
"throttled functions should only be able to be called again after the specified time"
Here is the testing algorithm...
let counter = 0;
const incr = () => counter++;
const throttledIncr = throttle(incr, 32);
throttledIncr();
throttledIncr();
setTimeout(() => {
expect(counter).to.eql(1);
throttledIncr();
setTimeout(() => {
expect(counter).to.eql(2);
done();
}, 32);
}, 32);
"throttled functions return their value"
Here is the testing algorithm...
let counter = 0;
const incr = () => ++counter;
const throttledIncr = throttle(incr, 32);
const result = throttledIncr();
setTimeout(() => {
expect(result).to.eql(1);
expect(counter).to.eql(1);
done();
}, 64);
"throttled functions called repeatedly should adhere to time limitations"
Here is the testing algorithm...
const incr = () => ++counter;
const throttledIncr = throttle(incr, 64);
const results = [];
const saveResult = () => results.push(throttledIncr());
saveResult();
saveResult();
setTimeout(saveResult, 32);
setTimeout(saveResult, 80);
setTimeout(saveResult, 96);
setTimeout(saveResult, 180);
setTimeout(() => {
expect(results[0]).to.eql(1);
expect(results[1]).to.be(undefined);
expect(results[2]).to.be(undefined);
expect(results[3]).to.eql(2);
expect(results[4]).to.be(undefined);
expect(results[5]).to.eql(3);
done();
}, 192);
My questions regarding each case:
How do I prevent the function from being called again ?
Why ISNT my function returning value? I can't deduce what or how to return a value with the given testing algorithm.
What does "throttled functions called repeatedly should adhere to time limitations" even mean? This seems contradictory to the first error. There isn't any mention of setting a time limit so I don't believe using setTimeout here is what they mean...
How do I prevent the function from being called again ?
function throttle(func, wait) {
function inner(...args) {
setInterval(func(...args), wait);
}
return inner;
}
First, your code above does not do what you expect it to do. Currently every time you invoke throttle, you are adding func to the event loop, to be executed on an interval.
So when you call throttleIncr 5 times, you are adding incr to the eventloop to be called five times.
One approach (imo), would be to keep track of the last time that throttle(func) was invoked. The next time throttle(func) is invoked, check to see if the wait time has elapsed. If so, invoke func and save off the new time. If not, return.
Why ISNT my function returning value? I can't deduce what or how to return a value with the given testing algorithm.
Your incr function, IS returning the value, however your throttle function puts it on the eventloop, for asychronous execution, so the return value is not available.
What does "throttled functions called repeatedly should adhere to time limitations" even mean? This seems contradictory to the first error.
This is not a javascript error, and likely a custom failure message from the tests you are invoking.
I tried something here, that seems to be working:
function throttle2(callback, delay = 1000) {
let interval;
let currentArgs;
return (...args) => {
currentArgs = args;
if (!interval) {
interval = setInterval(() => {
if (currentArgs) {
callback(...currentArgs);
currentArgs = null;
} else {
clearInterval(interval);
interval = false;
}
}, delay);
}
};
}
Sandbox: https://codesandbox.io/s/competent-tereshkova-ccop2e?file=/index.js:167-179
Related
I wanted to directly call a function (like interrupt handler) when a certain condition is met. I didn't want to using "polling" for that as it increases time complexity.
count = 1
p = new Promise((resolve, reject)=>{
if(count == 2){
resolve("hello")
}
});
p.then((msg)=>{
console.log(msg)
})
console.log("1 now");
count = 2;
I expected console.log(msg) to run when count=2 but this is not the case. It turned out that the promise is still "pending". What is the reason this happens? And how do I implement my question.
You can use a Proxy to listen variable changes.
const count = new Proxy({
value: 0
}, {
set(target, prop, val) {
// set value
target[prop] = val;
console.log(`count is ${val}`);
// stop condition
if (val == 2) {
console.log(`count is 2(trigger stop condition)`);
}
}
});
// wait 2 seconds and change count.value to 1
setTimeout(() => count.value = 1, 2000);
// wait 2 seconds and change count.value to 2
// it triggers the stop condition
setTimeout(() => count.value = 2, 2000);
console.log("Waiting for changes ...");
reference: Listen to js variable change
Proxy is one of the solutions for this. But I post another approach for your case.
You can define a custom class or object, and work with that class. Also you register your custom listener for it, and do whatever.
This is a sample of my code. Maybe it will give you some ideas for your solution.
class MyObject {
constructor(value, func) {
this._value = value;
this._listener = func;
}
get value() {
return this._value;
}
set value(newValue) {
this._listener(newValue);
this._value = newValue;
}
}
function customListener(changedValue) {
console.log(`New Value Detected: ${changedValue}`);
}
const count = new MyObject(1, customListener);
count.value = 2;
The issue you're having is that the code inside the Promise resolves synchronously. It seems like you are assuming Promises are by default async, but that is a common async misconception. So, the code
if(count == 2){
resolve("hello")
}
resolves synchronously (that is, right after you declare count to be 1) so the Promise will never be resolved. If you want to asynchronously check for a condition without using libraries, you can use setInterval:
function checkForCondition(count, time){
return new Promise(resolve => {
const interval = setInterval(() => {
if (count == 2){
resolve("The count is 2!");
}
} , time);
});
}
If you call this function, the callback inside setInterval will be placed on the event loop every x ms, where x is equal to the time parameter.
I'm trying to figure out how to set time out for function inside the loop iteration in Ionic TypeScript application.
setInterval makes equal time interval with calling the function in endless repetition:
setInterval(() => {
this.myFunc1(val);
}, 800);
setTimeout gives required result if listed sequentially:
setTimeout(() => {
this.myFunc1(val);
}, 800);
setTimeout(() => {
this.myFunc1(val);
}, 1200);
but how to loop with time interval trough the updated list and wait while pass second value val to the function, or call myFunc1 when it will be finished in previous iteration:
async myFunc2() {
for (let val of this.myValueList) {
/// wait for 5 sec or wait for finishing process, then pass value calling function:
this.myFunc1(val);
}
}
setInterval is the correct choice here. What's missing is that you need to clear the interval. setInterval returns an id that you can pass to clearInterval which stops the iteration.
Here I'm passing data to console.log, waiting a second, then repeating till done.
const myValueList = [5,6,7,8,9,10];
let i = 0;
const id = setInterval(() => {
console.log(myValueList[i++]);
if (i === myValueList.length) {
clearInterval(id);
console.log("done!");
}
}, 1000);
Simplest solution for waiting approximately 5 seconds:
const wait = t => new Promise(r => setTimeout(r, t));
and then in your code you can just do:
async myFunc2() {
for (let val of this.myValueList) {
await wait(5000);
this.myFunc1(val);
}
}
If myFunc1 is async, and you just want to wait for it to finish executing before continuing the loop, then you would just do:
async myFunc2() {
for (let val of this.myValueList) {
await this.myFunc1(val);
}
}
I want to call a method 5 times in every one second. for example I have a methodA which i want to execute this method in 5 times in every 1 second. so how can i do it.
methodA(){
console.log("called")
}
timePeriod(){
setimeout....
}
You can do it with setInterval()
const test = setInterval(() => console.log("Hello method"),1000);
setTimeout( () => clearInterval(test), 5000);
Hope it is what you need!
let timePeriod = 1000 // 1000 ms
function methodA(){
console.log("called")
}
setInterval(methodA, timePeriod)
notice here that we pass the function as an argument without () which in other words we're referencing the function. Note that if you pass the function name along with () it means you're calling it independently
Also, keep in mind that setInterval works a little bit different than you might think as it's a macro task asynchronous function in the event loop. if you don't know about the event loop simply all you need to know that setInterval doesn't actually call the function unless the rest of you script gets fired + the time period you specify
setInterval = the time needed to fire the rest of the script + the time period you specify
and what you can do to test this is:
const tick = Date.now(), timeLog = (val='---') => console.log(`${val} \n Elapsed: ${Date.now() - tick} ms`)
setInterval(_ => timeLog('smth from setInterval'), 1000)
for(let i=1;i<1000000000;i++){
// this loop will run for 1B times so that we get a clear time delay
}
timeLog('smth from normal console.log')
const Comp= () => {
const methodA=()=>{
// statement here
}
useEffect(()=>{
let a = setInterval(methodA,1000/5)
return(()=>{
clearInterval(a)
},[])
})
return(<></>)
}
Try to use interval inside useEffect hook.
One could control both, the interval itself and also the condition for terminating the running interval, by writing a utility/helper function which enables not only clocked functions but also clocked methods.
function clocked(interval, isTerminate, proceed, target) {
let count = 0;
let intervalId = setInterval(() => {
proceed.call(target ?? null);
if (isTerminate(++count)) {
clearInterval(intervalId);
}
}, interval);
}
const obj = {
text: "The quick brown fox jumps over the lazy dog.",
log() {
console.log(this.text);
},
};
function doTerminateWhen(counter) {
return counter >= 5;
}
setTimeout(() => { console.log('1.001 seconds passed'); }, 1001);
clocked(200, doTerminateWhen, obj.log, obj);
setTimeout(() => { console.log('1 second passed'); }, 1000);
.as-console-wrapper { min-height: 100%!important; top: 0; }
setInterval is being used her to call the methodA function at each once second and modCall function has a for loop to iterate and call the setInterval the number of times specified in its paramter
function methodA(i){
console.log('call '+i)
}
function modCall(len){
for(let i=0;i<len;i++){
setInterval(()=>methodA(i),1000)
}
}
modCall(5) //5 is number of time you want to call the methodA
every time when i use input , my function send data to server and i get response, but if i want to write in field 'name' - Thomas Edison , i will send letter by letter
i try to put setTimeout function and if user still writing a string nothing will be send , but i does not work
#input="throttledSave"
throttledSave (e) {
let eva = e
let DELAY = 2000;
if(e.target.value){
return this.throttle(this.setDataFinalJSON, DELAY, eva);
}
},
throttle: function (callback, limit,eva) {
var wait = false;
var typingTimer;
return function (callback, limit,eva) {
clearTimeout(typingTimer)
if (!wait) {
callback(eva);
wait = true;
typingTimer = setTimeout(function () {
console.log('oh again')
wait = false;
}, limit);
}
}
}
every time it is work until DELAY , i don't know why, maybe clearTimeout does not work , i got stuck. I don't know why if i write some text so fast i got console.log('oh again')
You could do this with lodash debounce (https://lodash.com/docs/4.17.15#debounce) method:
Creates a debounced function that delays invoking func until after
wait milliseconds have elapsed since the last time the debounced
function was invoked. The debounced function comes with a cancel
method to cancel delayed func invocations and a flush method to
immediately invoke them. Provide options to indicate whether func
should be invoked on the leading and/or trailing edge of the wait
timeout. The func is invoked with the last arguments provided to the
debounced function. Subsequent calls to the debounced function return
the result of the last func invocation.
_.debounce(func, [wait=0], [options={}])
Example:
methods: {
throttledMethod: _.debounce(() => {
console.log('I only get fired once every two seconds, max!')
}, 2000)
}
Best to use the vue variant of lodash: https://www.npmjs.com/package/vue-lodash
Timeout just delays each input event (so that each one causes the request, just after some time) which is not what you want. The basic idea of implementing this is simple: store the time of the last input event in the model, and on input, send your requests only when timeout has passed, something like:
data () {
return {
...
lastInputTime: null,
inputTimeout: 1000 // ms
}
},
...
methods: {
throttledSave (e) {
const attemptTime = new Date();
if(this.lastInputTime && attemptTime - this.lastInputTime > this.inputTimeout) {
// get value, send request etc
}
this.lastInputTime = attemptTime;
}
Well, this is exactly what is called debounce, dreijntjens suggests a similar thing but using a library which allows to decorate your function.
PS Actually, such decorating is a better approach (unless you are planning to change inputTimeout in runtime) since you don't clutter your model with extra stuff specific to debouncing; you can make your own "decorator" (not in the strict sence, decorators are supposed to have special syntax, rather than being a function that gets your function and returns a modified one) if your project doesn't tree-shake libraries properly. Something like this:
function debounce(func, timeout) {
let lastTime = null;
return function() {
const attemptTime = new Date();
if(lastTime && attemptTime - lastTime > timeout) {
func.apply(this, arguments);
}
lastTime = attemptTime;
}
}
lodash's implementation is much more sophisticated since it supports several options.
How about using the lazy input model modifier?
VueJS prototype for delayed (lazy) input
Vue.prototype.lazyInput = function(e, delay) {
const self = this;
if (typeof delay == 'undefined') {
delay = 500;
}
const target = e.target;
if (self.lazyTimer) {
clearTimeout(self.lazyTimer);
self.lazyTimer = null;
}
self.lazyTimer = setTimeout(function(){
target.dispatchEvent(new Event('change'));
}, delay);
}
Usage:
<input v-model.lazy="{variableName}" #input="lazyInput($event)">
You can always use the native setTimeout()
methods: {
search: function (event) {
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
... XMLHttpRequest ...
}, 2000)
every 2000 msec sending request if no new data.
I'm trying to write a function that measures the execution time of another function:
export class Profiler {
public measureSyncFunc(fn: () => any): Promise<number> {
return new Promise<number>((resolve, reject) => {
let elapsed = 0;
let intervalId = window.setInterval(() => {
elapsed += 1; // this is never called
}, 1);
this.execFunc(fn)
.then((result: any) => {
window.clearInterval(intervalId);
resolve(elapsed);
});
});
}
private execFunc(fn: () => any): Promise<any> {
return new Promise<any>((resolve, reject) => {
resolve(fn());
});
}
}
Then I use it like that:
let array = generateRandomArray(100000);
instance.measureSyncFunc(bubbleSort(array))
.then((elapsed: number) => {
console.log(`end session: ${elapsed} seconds`);
resolve();
});
The bubbleSort function is synchronous and it takes several seconds to complete.
See code here:
The result in the console is "end session: 0 seconds" because the interval callback is never called.
Do you know how I can make it called ?
Thank you very much guys !
If the functions you want to measure will always be synchronous there's really no need to involve promises.
Since the function you want to test takes parameters you it's best to to wrap it in an arrow function in order to be able to call it with another context and not have to manage it's parameters yourself.
Something simple like this will do just fine.
function measure(fn: () => void): number {
let start = performance.now();
fn();
return performance.now() - start;
}
function longRunningFunction(n: number) {
for (let i = 0; i < n; i++) {
console.log(i);
}
}
let duration = measure(() => {
longRunningFunction(100);
});
console.log(`took ${duration} ms`);
If you want to measure the time it takes an async function (if it returns a promise) to resolve you can easily change the code to something like this:
function measurePromise(fn: () => Promise<any>): Promise<number> {
let onPromiseDone = () => performance.now() - start;
let start = performance.now();
return fn().then(onPromiseDone, onPromiseDone);
}
function longPromise(delay: number) {
return new Promise<string>((resolve) => {
setTimeout(() => {
resolve('Done');
}, delay);
});
}
measurePromise(() => longPromise(300))
.then((duration) => {
console.log(`promise took ${duration} ms`);
});
Note: This solution uses the ES6 Promise, if you are using something else you might have to adapt it but the logic should be the same.
You can see both examples working in the playground here.
Don't use setInterval to count milliseconds (It's inaccurate, lags, drifts and has a minimum interval of about 4ms). Just get two timestamps before and after the execution.
function measureAsyncFunc(fn: () => Promise<any>): Promise<number> {
const start = Date.now();
return fn.catch(() => {}).then(() => {
const end = Date.now();
const elapsed = end-start;
return elapsed;
});
}
For higher accuracy, replace Date.now by performance.now.
Have a look at timeFnPromise and the related test cases.
target function is wrapped and executed when the wrapped function is called
appends fulfillment / rejection handler to the underlying Promise that returns the target functions return value as "ret" and the elapsed time as "elapsedTime"
supports arguments by passing them through to the target function
Samples Usage:
const wrappedFn = timeFnPromise(aFunctionThatReturnsAPromise)
wrappedFn()
.then((values)=>{
const {ret, elapsedTime} = values
console.log(`ret:[${ret}] elapsedTime:[${elapsedTime}]`)
})
Also available via NPM module jschest.
Here's a simple wrapper function I wrote. It returns a Promise (via the async keyword), and so you can just call it with your promise. I added the time value as a property to the response. If you cannot have that value in the response, then you would need to remove it afterwards.
const stopwatchWrapper = async (promise) => {
const startTime = Date.now()
const resp = await promise
resp.executionTime = Date.now() - startTime
return resp
}
const axiosPromise = stopwatchWrapper(axios(reqSelected))
const response = await axiosPromise
console.log(response.executionTime)
It would be good to clarify that the proposed approaches by toskv only work with the resolution of a single promise. If we want to use Promise.all() the time result it returns is wrong.
Here is an example with the code that toskv developed, but using Promise.all()
Measure with Promise.all()
If someone needs to measure the time it takes to execute each of the promises executed with a Promise.all() the approach that can be followed is to make use of the interceptors and do the time measurements there