Function called on interval accesses global variable - javascript

I am asynchronously receiving data (from 0 to 100 points per second) that is passed to a high-startup-cost function, extendTraces(data), which updates the user interface. If I call extendTraces() upon generating every point, the user interface becomes unresponsive. I've found that it is much more efficient to call the function periodically and pass it an array of points, call it pointArray.
I can track how many points have been added to pointArray and call extendTraces(pointArray) every 20th addition:
//inside asynchronous function
pointArray.push(point);
if (this.pointArray.length == 20){
(<any>Plotly).extendTraces(this.pointArray);
this.resetPointArray();
}
But if I fill pointArray halfway and don't receive any data for a while, I'd also to call extendTraces.
My solution is to call extendTraces() every second
//inside a function that is called when the page loads
window.setInterval(function() {
if (pointArray.length > 0){
(<any>Plotly).extendTraces(this.pointArray);
this.resetPointArray();
}
}, 1000);
My function that receives the points will simply concatenate them onto the pointArray.
//inside asynchronous function
pointArray.push(point);
I am new to js and was wondering if I'm using the correct paradigms for this task. I see a lot of information about callbacks and promises which I don't fully understand but I am suspicious that I'm doing something wrong by not using them. Coming from c++, I am concerned that two functions, the function defined in setInterval and the asynchronous function that receives points, both have access to the pointArray without any hardcoded mutex.

I would be tempted to wrap the logic into its own class, where it simply allows you to specify
After how many adds to call the method
After how long of inactivity to call the method
the method to call
function PointHandler(flushSize, flushTime, flushCallback){
var pointArray = [];
var lastFlush = setTimeout(() => this.flush(),flushTime)
this.addPoint = function(point){
pointArray.push(point);
if(pointArray.length == flushSize){
this.flush();
}
clearTimeout(lastFlush)
lastFlush = setTimeout(() => this.flush(), flushTime);
}
this.flush = function(){
flushCallback(pointArray);
pointArray = [];
clearTimeout(lastFlush)
lastFlush = setTimeout(() => this.flush(), flushTime);
}
}
var handler = new PointHandler(10, 5000, points => console.log(points));
document.getElementById("clickme").addEventListener("click", () => handler.addPoint(new Date()));
<button id="clickme">Add point</button>
The above code will call the callback after 5 seconds of inactivity, or when it gets 10 points added.
My callback simply console.log the current points, but you could call your method.

You should create a debounce function, this basically limits how often the function can be called. Here is the debounce function from the underscore library:
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, result;
var later = function(context, args) {
timeout = null;
if (args) result = func.apply(context, args);
};
var debounced = restArguments(function(args) {
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
timeout = _.delay(later, wait, this, args);
}
return result;
});
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
Now just wrap your extendTraces function around the debounce function and then call the function that is return from it.
Source: https://github.com/jashkenas/underscore/blob/master/underscore.js#L887-L914

Related

How to send data with delay vue js?

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.

How to make a delay for a function but don't execute again in Javascript

I'm making a shooting game in Javascript: the player will shoot one bullet and will delay for 2 seconds before shooting another one again. I don't know how to make a delay. Using setTimeout, it has a delay, but it will automatically call the function. Can anyone help me?
If I understand the question, you want a system that prevents a function, say shootBullet() from being called until a delay is finished. The simplest way to do this is a global variable and a setTimeout:
var canShoot = true;
function shootBullet() {
if (canShoot) {
//Shoot bullet
}
canShoot = false;
setTimeout(function() {
canShoot = true;
}, 2000);
}
If I'm not misunderstanding, in this case, you need a function which was called "debounce" like this :
function debounce(func, wait) {
let timeout;
return () => { //create closure
let context = this,
args = arguments;
let excuteFunction = () => {
func.apply(context, args)
}
clearInterval(timeout);
timeout = setTimeout(excuteFunction, wait);
};
}
when func is your shooting function and wait is the delay time.
You can read more about the debounce technique at here
Or you can use the built-in debounce function in Lodash: Lodash Docs

What is the right way to reset a timeout?

so I have a function like this:
function blabla(){
...
setTimeout(() => {
//do some stuff
}, 10000)
}
now How can I reset the time of the timeout (10000) if function was called and timeout was not finished yet?
I tried to kill the timeout if it does exist like this:
function blabla(){
...
if(to){
clearTimeout(to)
}
let to = setTimeout(() => {
//do some stuff
}, 10000)
}
but I get error that to is undefined. so what is the right way to check if a timeout exists or not. is there a better way to do this?
You just need declare to before the if, so that it exists when the if runs (and there is not undefined). You don't have to give it an actual value until later.
Realistically, you probably want to declare it outside the function, so it will persist next time you call the function.
Here's a runnable demo. Notice that despite calling blablah() twice, you only see "hello" once, because the second call to the function cancelled the original timeout.
var to;
function blabla() {
//...
if (to) {
clearTimeout(to)
}
to = setTimeout(() => {
//do some stuff
console.log("hello");
}, 10000)
}
blabla();
blabla();
dont use let, let scope is inside the function block.
if you call the function the second time, the function does not have let to defined.
use var so it is accessible within across function call.
Not good idea use global var for that, because it is not reusable.
Better write wrapper for that function, because it is common pattern. This native code or use npm packet for that
Debounce functions are included in many JavaScript libraries. The goal
behind each implementation is to reduce overhead by preventing a
function from being called several times in succession. Regardless of
the library, all debounce functions are built on JavaScript's native
setTimeout function.
https://www.npmjs.com/package/debounce:
function debounce(func, wait, immediate) {
let timeout;
return function() {
let context = this,
args = arguments;
let later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
var blabla = debounce(function(){
console.log(5)
}, 5000);
blabla()
blabla()

JS - Set delay between sending AJAX request

I am working on building a search feature into my site. To do this I have a search bar which sends an AJAX request to my server each time a user types something. The server will in turn send back the items which match the search.
The only problem with this currently is that if a user types "a" and then "b" what it will send is:
a
ab
To counter this I have found setTimeout which delays when the user enters a search; however, currently it is only delaying when the strings fire (i.e. waits 0.75 seconds before sending a and then waits 0.75 seconds before sending ab).
Here's the JS:
$('#searchBox').keyup(function(e) {
var timeoutID = setTimeout(searchRequest, 750, $(e.currentTarget).val());
});
function searchRequest(str) {
if (str.length > 0) {
console.log('search request initalized | sending: ', str);
var xhttp = new XMLHttpRequest();
xhttp.open('POST', 'code to send here', true);
xhttp.send(str);
}
}
I think what you need is a debounce function.
Here's the basic JavaScript debounce function (as taken from Underscore.js):
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
You can use it to debounce click:
$('#searchBox').keyup(debounce(function(e) {
searchRequest$(e.currentTarget).val());
}, 750));
You have to clear thew timeout to make it work. Have a look on this link ;) Resetting a setTimeout
Basically, when the user added a letter, you check if you already defined a timeout. If you defined a timeout you clear it. Then you reset the timeout. Something like that
var yourTimeout;
function sendInfo(info){
if (yourTimeout != undefined)
clearTimeout(yourTimeout);
yourTimeout = setTimeout(function(){
//do something
}, 500);
}
In your case, the sendInfo function is the keyup handler, and you call searchRequest in the timeout like you already did ;)
Hope it helps

Wait before a javascript function can be called again

I've looked at many different solutions to this, none of which worked. I know it has something to do with setTimeout, but I don't know how to implement it properly.
function myfunction()
{
//the function
//wait for 1 second before it can be ran again
}
To clarify: I don't want to call the function at a regular interval, I want to be able to enforce a delay before the function can be called again.
var lastTime = 0;
function myFunction() {
var now = new Date().getTime(); // Time in milliseconds
if (now - lasttime < 1000) {
return;
} else {
lastTime = now;
}
// rest of function
}
You don't need to use setTimeout at all. The following is similar to other answers, but uses a closure to remember the last time the function ran rather than a global variable.
var myFunction = function() {
var lastTime = new Date();
return function() {
var now = new Date();
if ((now - lastTime) < 1000) return;
lastTime = now;
/* do stuff */
};
}());
I think the easiest solution would be to hold a boolean variable and reset it to true after a given delay.
fiddle
HTML
<button id="clickme">click me!</button>
JavaScript
var canGo = true,
delay = 1000; // one second
var myFunction = function () {
if (canGo) {
canGo = false;
// do whatever you want
alert("Hi!");
setTimeout(function () {
canGo = true;
}, delay)
} else {
alert("Can't go!");
}
}
$("#clickme").click(function(){
myFunction();
})
With this, you hold a boolean, canGo, and set it to true. If the function is run, it sets canGo to false and sets a setTimeout() for a time period of delay, in milliseconds. If you try to run the function again, it won't run and will, instead, alert("Can't go!"). This was just for demonstrative purposes; you don't need that part. After delay, canGo will be set to true, and you will be able to once more run the function.
var lastRan = 0;
var myFunction = function() {
var now = Date.now();
if(now-lastRan < 1000) {
return;
}
lastRan = now;
//rest of function
};
You may want to use throttle or debounce from underscore.js
http://underscorejs.org/#throttle
throttle_.throttle(function, wait, [options])
Creates and returns a
new, throttled version of the passed function, that, when invoked
repeatedly, will only actually call the original function at most once
per every wait milliseconds. Useful for rate-limiting events that
occur faster than you can keep up with.
By default, throttle will execute the function as soon as you call it
for the first time, and, if you call it again any number of times
during the wait period, as soon as that period is over. If you'd like
to disable the leading-edge call, pass {leading: false}, and if you'd
like to disable the execution on the trailing-edge, pass {trailing:
false}.
var throttled = _.throttle(updatePosition, 100);
$(window).scroll(throttled);
http://underscorejs.org/#debounce
debounce_.debounce(function, wait, [immediate])
Creates and returns a
new debounced version of the passed function which will postpone its
execution until after wait milliseconds have elapsed since the last
time it was invoked. Useful for implementing behavior that should only
happen after the input has stopped arriving. For example: rendering a
preview of a Markdown comment, recalculating a layout after the window
has stopped being resized, and so on.
Pass true for the immediate parameter to cause debounce to trigger the
function on the leading instead of the trailing edge of the wait
interval. Useful in circumstances like preventing accidental
double-clicks on a "submit" button from firing a second time.
var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);
If you just want to run your function again after a set time, you can use setTimeout and pass it the function to run and the delay period in milliseconds.
function myfunction() {
//the function
//run again in one second
setTimeout(myfunction, 1000);
}
Edited based on poster's comments:
var waiting = false;
var myfunction = function() {
if (!waiting) {
//Run some code
waiting = setTimeout(function() {
waiting = false;
}, 1000);
}
};

Categories

Resources