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()
Related
I am executing the function delayFilter() on keyup of an input box. I want to delay 1 second after the user stops typing and run the function filterProducts(). However, when executing filterProducts() inside of the setTimeout I get the console error "this.filterProducts is not a function". This function gets called fine when outside of the setTimeout. Why is this error being thrown?
delayFilter() {
let timeout = null;
clearTimeout(timeout);
timeout = setTimeout(function() {
this.filterProducts();
}, 1000);
}
filterProducts() {
//do stuff
}
That is because these, this inside the callback, does not refer to the object outside.
Try this:
delayFilter() {
let timeout = null;
clearTimeout(timeout);
let self = this;
timeout = setTimeout(function() {
self.filterProducts();
}, 1000);
}
filterProducts() {
//do stuff
}
You can also try the arrow function. The reason can be seen here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords.
delayFilter() {
let timeout = null;
clearTimeout(timeout);
timeout = setTimeout(() => {
this.filterProducts();
}, 1000);
}
filterProducts() {
//do stuff
}
You need to bind the scope of the function in the setTimeout callback. the easiest way is to use arrow functions, if your platform supports it.
timeout = setTimeout(() => this.filterProducts(), 1000);
you can also save the scope in a variable.
var self = this;
timeout = setTimeout(funciton() { self.filterProducts() }, 1000);
Alternatively you can manually bind it. This is useful if you want to pass the function around.
timeout = setTimeout(function() {...}.bind(this), 1000);
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
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
I have a computationally heavy section of code which performs filtering of a data-set by DOM-manipulations. I made it async in order to ensure acceptable performace for large number of fiter items:
.on('input', function {
setTimeout(function(){
// heavy computation
}, 0)
})
The problem is that this section probably run on every user input change but only the result of the last one is of interest. So, my question is if there is a way to cancel the previous "threads" executing the heavy computation section and only execute the latest?
The best idea I have thus far is to use some sort of semaphore (possibly $.Deferred) and try to reduce the critical section.
If you only need the final result, use debounced event handler. This is a sample debounce implementation, but there are other on the web.
Wrapping your event handler with debounce will prevent the event handler from firing, as long as it's called repeatedly. The handler will fire, only if the handler is called, and then it's idle for a set amount of time.
.on('input', debounce(function {
// heavy computation
}, 500)) // wait 500ms before firing the handler
In the example type continuously and stop, you'll see that the console will log the word called only once:
var debounce = function(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 || 200);
if ( callNow ) {
func.apply(context, args);
}
};
};
$('#input').on('input', debounce(function() {
console.log('called');
}));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="input">
I think im missing something fairly obvious with how the clearInterval method works.
So with the code below. I would expect the first function call to execute testFunction and set the interval to repeat the function. The 2nd call would execute the second function which will remove the interval from the 1st function. As this would execute far before the 5000ms interval the first function would not be executed again. However it does not behave like this.
Could someone please explain what is wrong with my method?
Reason for this is in a program I am writing I am making repeated get requests, every 30 seconds or so , using setTimeout but i would like a method to easily remove this interval at other points in the program
function testFunction() {
$("#test").append("test");
setTimeout(testFunction, 5000);
}
function stopFunction() {
clearTimeout(testFunction);
}
testFunction();
stopFunction();
setTimeout returns an ID so you should
var timeoutID = setTimeout(blah blah);
clearTimeout(timeoutID);
setTimeout returns an object that you need to pass into the clearTimeout method. See this article for an example: http://www.w3schools.com/jsref/met_win_cleartimeout.asp
setTimeout returns an identifier for the timer. Store this in a variable like:
var timeout;
function testFunction(){
...
timeout = setTimeout(testFunction, 5000);
}
function stopFunction(){
clearTimeout(timeout);
}
Here is a simple and I think better implementation for this .
var _timer = null,
_interval = 5000,
inProgress = false,
failures = 0,
MAX_FAILURES = 3;
function _update() {
// do request here, call _onResolve if no errors , and _onReject if errors
}
function _start() {
inProgress = true;
_update();
_timer = setInterval(_update, _interval);
}
function _end() {
inProgress = false;
clearInterval(_timer);
}
function _onReject(err) {
if (failures >= MAX_FAILURES) {
_end();
return false;
}
_end();
failures++;
_start();
}
function _onResolve(response) {
return true;
}