I did some research and found busy wait solutions. I don't want the processor to execute a loop in the script until I get a keypress event through JQuery.
How can I pull of something like sleepUntilKeyPress(), which is a function like sleep() in C but instead of waiting some especific time it waits for a keypress?
You're thinking backwards. Your function shouldn't wait for a keypress to do something, the function should do something whenever a key is pressed. Maintain your data state in a scope that your key callback can reach and update your state on keypress.
You're trying to say:
while(!keypress){
dontDoSomething();
}
You should be doing something more like:
on(keypress, doSomething);
Consider this idea:
var counter, ispressed, el;
// Bad
counter = 0;
ispressed = false;
el = document.getElementById("element");
el.addEventListener("mousedown", function () {
ispressed = true;
});
el.addEventListener("mouseup", function () {
ispressed = false;
});
while (1) {
if (ispressed) {
counter += 1;
}
}
// Better
el.addEventListener("click", function () {
counter += 1;
});
Let's forget about the fact that that while will lock up the program, (I don't even think the event listeners will trigger and I'm pretty sure the DOM will lock up), you're asking your program to waste time constantly checking to see if you're pressed a button yet. Rather than do that, let the browser check for you with the built in addEventListener() and do what you would normally do when you find out that the button has been pressed.
var interval = window.setInterval(function () {
// hang around and do nothing
}, 1000);
document.onkeypress = function () {
if (/* some specific character was pressed */) {
window.clearInterval(interval);
// do some other thing, other thing
}
};
Related
I have some text boxes that uses AJAX and will process what the user types as they're typing. The problem is that the process that happens after they type is a pretty loaded one. Is there anyway that I can make the event wait 500 msec or so before it will process the event again? So let's say I type a, and then 200ms after I type bsdke. The event will only process a coming in. Let's say I then type asdf 600ms after I typed a. The event will then trigger and process absdkeasdf because it has been 500ms after the last process.
Or even, is there a way I can make the event not become triggered until the previous event that was triggered has finished what it was doing?
Is it possible? Surely sticking a timeout function at the end isn't going to work, right?
$('#selection1-3-4-a, #selection1-3-4-b, #selection1-3-4-c, #selection1-3-4-d, #selection1-3-4-e, #selection1-3-4-f').on('input', function (e) {
var url = "/ex.php";
$.ajax ({
//......
});
It can get hard to tune the event so the users gets a nice experience and you don't flood the server with requests when using autocomplete functions.
I would do it this way:
The event will wait X miliseconds to run itself. If the event is fired again, and the X miliseconds haven't passed, you reset the delay to 0.
Let's be cool and make a generic function to do this (couldn't test it, it might be broken):
function delayedEvent(eventHandler, delay) {
var lastTimeout = null;
return function () {
var that = this,
args= Array.prototype.slice.call(arguments).sort();
if (lastTimeout !== null)
window.clearTimeout(lastTimeout);
lastTimeout = window.setTimeout(function () {
eventHandler.apply(that, args);
}, delay);
};
}
$('... selector ...').on('event', delayedEvent(function () { ... your code ... }, 500));
Use setTimeout:
var timer;
$('#selection1-3-4-a, #selection1-3-4-b, #selection1-3-4-c, #selection1-3-4-d, #selection1-3-4-e, #selection1-3-4-f').on('input', function (e) {
var url = "/ex.php";
clearTimeout(timer);
timer = setTimeout(function() {
$.ajax ({
//......
}, 500);
});
I would recommend using a lock.
// global lock
var locked = false;
$('#selection1-3-4-a, #selection1-3-4-b, #selection1-3-4-c, #selection1-3-4-d, #selection1-3-4-e, #selection1-3-4-f').change(function (e)
{
if(!locked)
{
locked = true;
$.ajax(......) // ajax here
setTimeout(function(){locked = false;}, 500);
}
});
Im listening for an event and I need to run a function (in this example console log for demoing my code) when it happens.
This is working however the event happens multiple times in quick succession and I only want the function to run once. How can I run the function straight away but then wait a second before its able to be triggered again?
$(document).on('someEvent', function(event, data) {
if (var === 'something') {
console.log('Run');
}
});
Update: To be clear, I need to wait for the event 'someEvent' to occur before my console function runs.
Some like that?
var is_blocked = false;
var block = function( time_to_wait ) {
is_blocked = true;
setTimeout( function() {
is_blocked = false;
}, time_to_wait );
};
$(document).on('someEvent', function(event, data) {
if ( is_blocked === false ) {
block( 1000 );
console.log('Run');
}
});
If you don't mind using an external library, use lodash's debounce method. Note that the sample in the docs is pretty similar to the case you described. The options (leading/trailing) can be used to customize the behavior.
There's a tiny library called underscore.js that has a ton of useful functions. Among these there is _.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 argument 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.
In your case it's a matter of wrapping the handler function like this (I used 100ms for the timeout):
$(document).on('someEvent', _.debounce(function(event, data) {
if (var === 'something') {
console.log('Run');
}
}, 100));
Function "functionToBeCalled()" will be executed immediately, and every 0.4 seconds. If you want to call again that function after 0.4s and not every time replace setInterval with setTimeout.
var variable = "something";
$("#button").on('click', function(event, data) {
if ( variable === 'something') {
console.log('Run');
setTimeout(function(){
$("#button").trigger("click");
}, 1000)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="button">Button</div>
You could use the current time:
var waitUntil = Date.now();
$(document).on('someEvent', function(event, data) {
if (Date.now() >= waitUntil) {
waitUntil = Date.now() + 5000 // 5 seconds wait from now
console.log('Run');
}
});
Here is a fiddle which uses a button click as the event, and gives feed-back on-screen about whether the event is treated or not.
Here's a neat little function that might help:
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
}
I am trying to create the following functionality in my javascript:
$("mySelector").each(function(){
// Do something (e.g. change div class attribute)
// call to MyFunction(), the iteration will stop here as long as it will take for myFunction to complete
});
function myFunction()
{
// Do something for e.g. 5 seconds
}
My question is how can I stop every iteration for the duration of the myFunction()?
No, that isnt possible. You'll have to code it differently, possibly with a setTimeout based on the current index of .each.
$("mySelector").each(function(i){
// Do something (e.g. change div class attribute)
// call to MyFunction(), the iteration will stop here as long as it will take for myFunction to complete
setTimeout(myFunction,i*5000);
});
function myFunction()
{
// Do something for e.g. 5 seconds
}
Edit: You can also do it with queuing: http://jsfiddle.net/9Bm9p/6/
$(document).ready(function () {
var divs = $(".test");
var queue = $("<div />");
divs.each(function(){
var _this = this;
queue.queue(function(next) {
myFunction.call(_this,next);
});
});
});
function myFunction(next) {
// do stuff
$(this).doSomething();
// simulate asynchronous event
var self = this;
setTimeout(function(){
console.log(self.id);
// go to next item in the queue
next();
},2000);
}
Here's a jsFiddle that I think will do what you need:
http://jsfiddle.net/9Bm9p/2/
You would just need to replace the selector with what you use.
The "loop" that is occurring will wait for myFunction to finish before moving on to the next element. I added the setTimeout inside of myFunction to simulate it taking a period of time. If you are using asynchronous things, such as an AJAX request, you would need to put the call to myFunction inside of the complete method...or in the callback of an animation.
But as someone already commented, if everything in myFunction is synchronous, you should be able to use it as you are. If you are looking for this process to be asynchronous, or if things in myFunction are asynchronous, you cannot use a for loop or .each().
(function () {
"use strict";
var step = 0;
var content = $("mySelector");
var max = content.length;
var speed = 5000; // ms
var handle = setInterval(function () {
step++;
if (step >= max) {
clearInterval(handle);
} else {
var item = content[step];
// do something
}
}, speed);
}());
setInterval will do it once-every-n-miliseconds, and clearInterval will stop it when you're done. This won't lock up the browser (provided your "do something" also doesn't). FRAGILE: it assumes that the results of $("mySelector") are valid for the duration of the task. If that isn't the case then inside do something then validate item again.
I have a setInterval calling a loop which displays an animation.
When I clearInterval in response to a user input, there are possibly one or more loop callbacks in queue. If I put a function call directly after the clearInterval statement, the function call finishes first (printing something to screen), then a queued loop callback executes, erasing what I wanted to print.
See the code below.
function loop() {
// print something to screen
}
var timer = setInterval(loop(), 30);
canvas.onkeypress = function (event) {
clearInterval(timer);
// print something else to screen
}
What's the best way to handle this? Put a delay on the // print something else to screen? Doing the new printing within the loop?
Edit: Thanks for the answers. For future reference, my problem was that the event that triggered the extra printing was buried within the loop, so once this executed, control was handed back to the unfinished loop, which then overwrote it. Cheers.
You could also use a flag so as to ignore any queued functions:
var should;
function loop() {
if(!should) return; // ignore this loop iteration if said so
// print something to screen
}
should = true;
var timer = setInterval(loop, 30); // I guess you meant 'loop' without '()'
canvas.onkeypress = function (event) {
should = false; // announce that loop really should stop
clearInterval(timer);
// print something else to screen
}
First of all, you probably meant:
var timer = setInterval(loop, 30);
Secondly, are you sure calling clearInterval does not clean the queue of pending loop() calls? If this is the case, you can easily disable these calls by using some sort of guard:
var done = false;
function loop() {
if(!done) {
// print something to screen
}
}
var timer = setInterval(loop(), 30);
canvas.onkeypress = function (event) {
clearInterval(timer);
done = true;
// print something else to screen
}
I need to have some functionality in my web app where a specific action occurs when the user clicks and holds on an element. Think of it like the long press on Android.
I have my div:
<div id="myDiv"
onmousedown="press()"
onmouseup="cancel()"
onmouseout="cancel()"
onmousemove="cancel()">Long Click Me</div>
and my javascript:
var down = false;
function press()
{
down = true;
setTimeout(function() { action(); }, 1500);
}
function cancel()
{
down = false; // this doesn't happen when user moves off div while holding mouse down!
}
function action()
{
if (!down)
return; // if the flag is FALSE then do nothing.
alert("Success!");
down = false;
}
This works as long as all I do is press and hold on the element. I have the onmouseout and onmousemove events to call cancel() because I want the user to have the option to change their mind and move the mouse off the element before action() starts.
Unfortunately, it seems that my code does not do this.
In fact, if the use clicks down for a moment, moves the mouse off the div and releases before the 1.5 sec then action() won't bail out as expected.
Edit: Thanks for your input everyone but it turns out I'm just a little bit special and didn't see that I forgot a capital letter in my HTML in my onmouseout. The sample code I gave above should work exactly as expected.
Of course action() is still called. You didn't actually cancel the setTimeout() function. I would suspect that maybe in your real code, you have a scoping issue and maybe aren't testing the same version of the done variable that you think you are.
A better way than using the down flag would be to keep track of the return value from setTimeout() and actually cancel the timer in the cancel() function. Then, action() will never fire when you don't want it to. I think it's also technically a more correct behavior when you mouseout to cancel any chance of the timer firing.
Also, there is no such thing as:
bool down = false;
in javascript. It would have to be:
var down = false;
I would recommend this code:
var downTimer = null;
function down()
{
cancel();
downTimer = setTimeout(function() { action(); }, 1500);
}
function cancel()
{
if (downTimer)
{
clearTimeout(downTimer);
downTimer = null;
}
}
function action()
{
downTimer = null;
alert("Success!");
}
You also need to clear the timeout in your cancel function - otherwise it will still fire - as you initiated it in the down function.
so..
bool down = false;
//your timeout var
var t;
function down()
{
down = true;
t = setTimeout(function() { action(); }, 1500);
}
function cancel()
{
down = false;
clearTimeout(t);
}
function action()
{
if (!down)
return;
alert("Success!");
down = false;
}
There are several things wrong with your code as I see it.
First
bool down = false;
is not valid JavaScript. It should be
var down = false;
Then you have two variables called down: the boolean value and the function. The function will overwrite the variable, until as such time that you execute one of the statements that sets down to true or false.
As others have said: once set, the deferred function will continue to be executed 1.5 seconds later, unless you cancel the timeout. But then again it doesn't matter, since you do check to see if the mouse button is down before doing anything.
So I'd say rename the boolean variable to isMouseDown or something and try again.
to cancel the timeout in the cancel() function, use
var mytimer = null;
function ondown(){mytimer = setTimeOut('action()', 1500;)}
function cancel(){clearTimeout(mytimer);}
function action(){mytimer=null; alert('success!');}
also note that you used down first as a variable end then as a function... Calling if(!down) will always return false because down refers to a function.