"Run if I haven't been called in x seconds" in JavaScript - javascript

Suppose I have a callback firing perpetually as the result of some event; i.e. Someone's moving a mouse.
I'd like to run a cleanup action if the callback hasn't fired in x seconds; i.e. If they haven't moved the mouse in 2 seconds, fire.
I think I could probably fix something up with setTimeout, but I'm wondering if any standard libraries have a function for this? Sort of a 'dead-mans-switch', seems like it would be common enough to have a standard method. If not I'm making one. Anyone?

De-bouncing may be a technique that will help.
It is essentially a method of wrapping a function so that you have control over when the wrapped function will execute, regardless of how often the debounced version is called.
This is most commonly used for events, like window resize. Then you can only execute your handler once the user has finished resizing the window rather then whilst they are resizing it.
There is also throttling, this is similar but has important differences.
Throttled functions will execute once every n time rather than a debounced version which will executed after it hasn't be called for n time.
underscore and lodash have implementations of de-bouncing and throttling.
However they it is quite easy to achieve and you don't really need a large library if its not already being used.

I think you're on the right track about setTimeout. As per your wonder, I am not aware of a module that would do it. And due to the intrusive nature of this process, it makes sense.
You could do this tho:
var yourmodule; //assuming you're using a module to store your app code; the object should obviously exist before continuing
yourmodule.cleanupSequenceId = -1;
function yourEventCallback() {
if (yourmodule.cleanupSequenceId !== -1) clearTimeout(yourmodule.cleanupSequenceId);
//function logic
//cleanup:
yourmodule.cleanupSequenceId = setTimeout(cleanupMethod, 2000);
}

After stumbling upon this (very old) question, and reading many others like it, I found a solution that works for me so I wanted to share it.
You define a "Debounce" function like this:
var debounce_timeout // Global debouncer timer, so all calls target this specific timeout.
function debounce(func, delay = 2000) {
clearTimeout(debounce_timeout)
debounce_timeout = setTimeout(() => {
func()
}, delay)
}
Now if you wish to debounce some function, you do:
debounce(myFunction)
Debouncing essentially means, that when your function is called, we observe for 'delay' duration, if any other calls to the function is made. If another call is made, we reset our observing time.

Related

Lock simultaneous JavaScript invocation

I have a simple JavaScript function that manipulates the DOM (heights of elements for layout reasons).
The function get's called on window's resize (throttled to 1s) and on button click.
In my function everything is wrapped inside a _.delay() function in order for the script to wait 1s for a triggered animation to finish.
The problem is that sometimes the function get's called fast on after another and the second call starts before the first call ending. Now the function calls are doing their things simultaneously and everything get's bad.
My question:
How can I tell the function to only run one at a time? Some kind of lock would be good that locks the second call from executing. It would be great if this second call still executes, but only after the first call remove the lock.
Is something like this possible in JavaScript and how?
EDIT
Here is a code example of how the script looks like:
function doStuff() {
var stuff = $('[data-do-stuff]');
var height = stuff.height();
// Add CSS class that changes height of stuff
// Class starts an animation of duration of 1s
stuff.addClass('active-stuff');
// Wait 1s for the animation started by added class
_.delay(function() {
stuff.height(height * 42);
}, 1000);
}
$(window).on('resize', _.throttle(function() {
doStuff();
}, 1000));
$('.tasty-button').on('click', function() {
doStuff();
});
This is not a working example, just a gist of what the general structure of my script is.
If I e.g. click multiple times on the tasty button (about 3x in 1s) it messes with everything. (In my real script, I have got more trigger so just disabling the button for 1 second doesn't do the trick -.-)
I would like it to behave like this: If doStuff executes, lock every call until doStuff finishes with executing and then execute the locked calls afterwards.
PROMISES in Javascript is what you are looking for.
Without code examples, it's hard to suggest solutions specific to your question. However, here's some thoughts on your overall problem:
What you're experiencing is a called a "race condition" where a part of your application depends on multiple functions finishing at undetermined times.
Generally, there are two ways to handle situations like this:
1) Use callbacks. About Callbacks
2) As another user suggested, use JS promises. About JS Promises

javascript setInterval not keeping it's timing properly

I've got multiple elements on my page that fade in and out on a timer using javascript setInterval to set them in motion. I have them delayed so they are offset just slightly to create a nice cascading effect, but if you leave the page open long enough, they all catch up to one another and the timing gets all messed up (you've got to leave it for a few minutes).
I have an ugly example of the issue at CodePen here: http://www.cdpn.io/wgqJj
Again, you've got to leave the page open and untouched for a few minutes to see the problem. If you had more items on the page (5 or 10) the problem becomes even more apparent. I've also used this type of effect with several jQuery photo rotator plugins, and over time, the issue always crops up.
Is there any explanation for this?
Here is the code I'm using (I know the javascript could be cleaner):
HTML:
<p id="one">First</p>
<p id="two">Second</p>
<p id="three">Third</p>
JavaScript:
$(document).ready(function() {
var timer1 = setTimeout(startOne,1000);
var timer2 = setTimeout(startTwo,2000);
var timer3 = setTimeout(startThree,3000);
});
function startOne () {
setInterval(flashOne,3000);
}
function startTwo () {
setInterval(flashTwo,3000);
}
function startThree () {
setInterval(flashThree,3000);
}
function flashOne () {
$("#one").fadeTo("slow",0.4).fadeTo("slow",1.0);
}
function flashTwo () {
$("#two").fadeTo("slow",0.4).fadeTo("slow",1.0);
}
function flashThree () {
$("#three").fadeTo("slow",0.4).fadeTo("slow",1.0);
}
Question has already been answered here. Quoting from the top rated answer in this topic:
it will wait AT LEAST 1000MS before it executes, it will NOT wait exactly 1000MS.
Giving an actual answer, I'd solve it like this:
$(function(){
setTimeout(flashOne,1000);
});
function flashOne () {
$("#one").fadeTo("slow",0.4).fadeTo("slow",1.0);
setTimeout(flashTwo,1000);
}
function flashTwo () {
$("#two").fadeTo("slow",0.4).fadeTo("slow",1.0);
setTimeout(flashThree,1000);
}
function flashThree () {
$("#three").fadeTo("slow",0.4).fadeTo("slow",1.0);
setTimeout(flashOne,1000);
}
Like this it's not possible for the timers to mess up, as it's always delayed one second after the item before has flashed.
Consider using a chained setInterval instead as this give a guaranteed slot to the browser. Reference this SO post..
Currently you only use setInterval to start the animation. From there jQuery is handling the "oscillations".
Theoretically using a chained set interval should guarantee a slot, to the browser. More importantly, you can hard code the offset into the code at each interval, instead of only once at the beginning.
The setTimeout() and setInterval() functions do not guarantee that your events run exactly on schedule. CPU load, other browser tasks, and similar can and will affect your timers, therefore they are not reliable enough for your use case.
A solution for this would be asynchronous events (promises or similar) or using the event queue that jQuery supplies. That way you could either nest with callbacks, or queue them up and then fire the queue over again once it hits the last item in the queue. The .queue() API documentation page has an example of this.

Game loop crashing my browser

I'm trying to write my first html5 game. However, the game loop causes my browser to become unresponsive (eventually being shut down by the browser). I created a state machine:
while(state != State.EXIT){
switch(state){
case State.SPLASH:
break;
case State.HOW_TO:
break;
case State.PLAY:
oldTime=Date.now();
state=gameLoop();
break;
case State.GAME_OVER:
break;
default:
state=State.EXIT;
}
}
That seems to be working okay. So, then, here's the game loop:
function gameLoop(){
var newTime=Date.now();
var delta=newTime-oldTime;
update(delta/1000);
render();
oldTime=newTime;
return state;
}
This is where the crash happens. If I take out the return statement, it returns null or whatever javascript returns. And, that's fine. It runs once and exits. However, if I leave it in there, this is where the browser seizes up. The update function gives my character the ability to move and the render function draws one image to the screen. Very simple stuff.
NOTE: This is being written in the canvas element if that matters.
SOLUTION! I created a stateSelector() function which contains the switch statement above(without the while). However, rather than state=gameLoop, I used interval=setInterval(gameLoop, 1). Then, I use clearInterval(interval) when I want to stop, followed immediately by stateSelector(). Obviously, if I want to change the state, I do that before calling the stateSelector function. I could probably have it take in a parameter containing the state I want to go into, but that a small change that I could evaluate later. I just wanted to announce my solution in case anyone else runs into this.
JavaScript is single-threaded and runs (in effect) in the GUI thread in all common browser environments. When you're JavaScript, the UI of the browser is not updated until the JavaScript finishes running.
You're using a while loop that will never finish, and so the UI will never get updated. To fix this, you need to restructure a little: render a frame and then tell the browser you want to render another frame soon; it can update the UI and do other browsery things and then it can get back to you to render another frame.
Implementation
There's an experimental new function called requestAnimationFrame that can do this. Since it's still experimental, to use it, you need to check for browser-specific versions of it, or if it's not available at all, provide a fallback. Here are some of the names of the browser-specific versions:
mozRequestAnimationFrame for Gecko (Firefox)
webkitRequestAnimationFrame for WebKit (Chrome and Safari)
msRequestAnimationFrame for Trident (Internet Explorer)
So if an unprefixed requestAnimationFrame is available, use that. If that's not available but a prefixed one is, use that. If none of those work, you can use a fallback:
function fallbackRequestAnimationFrame(func) {
setTimeout(func, 10); // Schedule func to be run in 10 milliseconds.
}
Here's a slightly-modified version of the code found on MDN:
var myRequestAnimationFrame =
window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame
|| fallbackRequestAnimationFrame;
Once you've figured out which requestAnimationFrame function you can use, you can change your game loop (which seems to be not the gameLoop function, which has no loops, but rather the while loop) to look like this:
function runFrame() {
switch(state) {
// state handling code
}
if(state != State.EXIT) {
myRequestAnimationFrame(runFrame);
}
}
Then start it off:
runFrame();
I think you may need some sort of a pause, if you're looping with no pause it will consume all of the CPU processing the loop over and over, preventing the page from rendering.
JavaScript runs on the same thread the browser uses to render the page, so if you write an infinite loop the browser never gets control back to refresh the page. (Modern browsers detect "long running" loops and offer the user a chance to abort them, but that doesn't help you with your game.)
You need to use either setTimeout() or setInterval() with some variation on the following:
function gameLoop() {
// do calculations
// render
// etc
if (!gameOver)
setTimeout(gameLoop, 30);
}
gameLoop();
(Note: your original gameLoop() function doesn't actually loop - your loop is controlled outside the function - whereas what I've just showed does loop.)
The setTimeout() function queues up a function to be run later and then immediately continues with the next line of code. When the current code finishes executing the browser then gets control back to update the display, etc. Then after (approximately) the specified interval (in milliseconds) the queued function is executed.
The effect above is similar to a recursive call where a function calls itself directly, except using setTimeout() yields control back to the browser in the meantime.
Outside the gameLoop() function you can then define event handlers for key and/or mouse events, and have those update variables that gameLoop() will use to decide how to, e.g., move the player's character, something like I said in this answer to another question.
Generally, programmers make loops play nice by adding sleep(s) or yield() calls, but since javascript's event driven model lacks these, you would instead replace your loop with a setInterval() which could call a function containing something like your loop body every specified interval, say, every 33 milliseconds, for a 30 fps experience.

Javascript: How do you make function2 execute only after function1 is completely finished?

I have some code, with the order of the functions in the order I want them executed. However, they seem to at the same time (they begin sequentially within microseconds of eachother presumably).
The problem is that some of the functions include animations. How do I make it so the next function doesn't execute until the previous functions are completely finished???
Following is some of my code. I created a callback chain thinking that it would be the solution, but indeed it is not. The functions themselves call jQuery animations. I'm guessing i need to use some type of notofication from jQuery to tell me when animations are done. Anyhow, any advice is greatly appreciated!
show_loader(0, function() {
close_box($target_close, '/', '#' + $target_close.attr('id') + ' .post_wrap', function() {
open_box($target_open, event.value, '.wide-col', function() {
hide_loader(function() {
scroll_to_content($target_open, function() {
});
});
});
});
});
To be precise, I want the scroll_to_content() function to be executed after all the previous actions have been completed in their entirety. Currently, it executes at the same time as everything else, and therefore my page scroll is completely off because the size of my content continues changing after scroll_to_content() is finished.
Callback chains are basically the solution but I suspect you're not threading your callbacks correctly in your calls to jQuery. $.animate(...) has an optional complete callback-- that's where you want to pass the function that should execute after your initial animation finishes. All of the Effects in jQuery UI should have a similar optional argument.
You can use a similar pattern yourself in order to chain event handlers, for instance:
function handler(event, callback) {
// do some work
// ...
callback();
}
This strategy for chaining function evaluations is called continuation-passing style and is handy in a lot of situations. Be careful using it, however, as many people find it more confusing to read than a traditional, sequential implementation.
http://api.jquery.com/queue/
Sorry, I don't have enough time to go into detail, but as the previous commenter said, queues are what you want to be focusing on to solve this problem.
you have 3 options:
1- Split your animations into multiple chained animate() calls.
This is an example to clarify it for you.
2- Follow the answer posted by #dml.
3- Try to use this plugin to add delays between your calls (don't know if it can fix this scenario or not, give it a try)
http://www.evanbyrne.com/article/jquery-delay-plugin

Using setTimeout to improve responsiveness

When looking to improve a page's performance, one technique I haven't heard mentioned before is using setTimeout to prevent javascript from holding up the rendering of a page.
For example, imagine we have a particularly time-consuming piece of jQuery inline with the html:
$('input').click(function () {
// Do stuff
});
If this code is inline, we are holding up the perceived completion of the page while the piece of jquery is busy attaching a click handler to every input on the page.
Would it be wise to spawn a new thread instead:
setTimeout(function() {
$('input').click(function () {
// Do stuff
})
}, 100);
The only downside I can see is that there is now a greater chance the user clicks on an element before the click handler is attached. However, this risk may be acceptable and we have a degree of this risk anyway, even without setTimeout.
Am I right, or am I wrong?
The actual technique is to use setTimeout with a time of 0.
This works because JavaScript is single-threaded. A timeout doesn't cause the browser to spawn another thread, nor does it guarantee that the code will execute in the specified time. However, the code will be executed when both:
The specified time has elapsed.
Execution control is handed back to the browser.
Therefore calling setTimeout with a time of 0 can be considered as temporarily yielding to the browser.
This means if you have long running code, you can simulate multi-threading by regularly yielding with a setTimeout. Your code may look something like this:
var batches = [...]; // Some array
var currentBatch = 0;
// Start long-running code, whenever browser is ready
setTimeout(doBatch, 0);
function doBatch() {
if (currentBatch < batches.length) {
// Do stuff with batches[currentBatch]
currentBatch++;
setTimeout(doBatch, 0);
}
}
Note: While it's useful to know this technique in some scenarios, I highly doubt you will need it in the situation you describe (assigning event handlers on DOM ready). If performance is indeed an issue, I would suggest looking into ways of improving the real performance by tweaking the selector.
For example if you only have one form on the page which contains <input>s, then give the <form> an ID, and use $('#someId input').
setTimeout() can be used to improve the "perceived" load time -- but not the way you've shown it. Using setTimeout() does not cause your code to run in a separate thread. Instead setTimeout() simply yields the thread back to the browser for (approximately) the specified amount of time. When it's time for your function to run, the browser will yield the thread back to the javascript engine. In javascript there is never more than one thread (unless you're using something like "Web Workers").
So, if you want to use setTimeout() to improve performance during a computation-intensive task, you must break that task into smaller chunks, and execute them in-order, chaining them together using setTimeout(). Something like this works well:
function runTasks( tasks, idx ) {
idx = idx || 0;
tasks[idx++]();
if( idx < tasks.length ) {
setTimeout( function(){ runTasks(tasks, idx); },1);
}
}
runTasks([
function() {
/* do first part */
},
function() {
/* do next part */
},
function() {
/* do final part */
}
]);
Note:
The functions are executed in order. There can be as many as you need.
When the first function returns, the next one is called via setTimeout().
The timeout value I've used is 1. This is sufficient to cause a yield, and the browser will take the thread if it needs it, or allow the next task to proceed if there's time. You can experiment with other values if you feel the need, but usually 1 is what you want for these purposes.
You are correct, there is a greater chance of a "missed" click, but with a low timeout value, its pretty unlikely.

Categories

Resources