How to end setTimeout after user input - javascript

Im developing a game in JavaScript in which the user needs to give a key input (press spacebar) when a clock hand moves slightly more than usual.
Currently, I am using a setTimeout function that gives the user 1 second to give a key input after the clock hand has ticked (rotated by 10 degrees).
If the user correctly presses space when the clock hand moves more than usual (15 degrees), an indicator will flash green, otherwise it will flash red.
The problem I am running into is that once the user gives an input within 1 second of the hand moving, the indicator will not flash until AFTER that 1 second has passed (ie, if the user gives an input after 0.4 seconds, the indicator will not flash until 0.6 later)
I know this is because the indicator is set up in my setTimeout fuction, which will only execute the code after 1 second. I have tried to test for the user input outside of the setTimeout function but that way the user does not get 1 second to give a response.
I was wondering if there is a way around this problem or a better way to approach this?
//Get input after clock tick
setTimeout(() => {
if (irregular_tick && space_pressed) {
flashScreenGreen();
}
if (!(space_pressed) && irregular_tick) {
flashScreenRed();
}
},1000);
Thanks for any help!

You'll need to keep a reference to your timer outside of the setTimeout callback and add a listener for the keypress with an interrupt callback which will clear the timeout if all conditions are met.
let timer = null;
let space_pressed = false;
function reset() {
timer = null;
space_pressed = false;
}
function interruptHandler(e) {
if (timer !== null) { // only look for spacebar if timer is running
space_pressed = e.key === ' ';
if (irregular_tick && space_pressed) {
// clear timeOut if successful
clearTimeout(timer);
reset();
flashScreenGreen();
}
}
}
document.body.addEventListener('keyup', interruptHandler);
timer = setTimeout(() => {
if (!space_pressed && irregular_tick) {
flashScreenRed();
}
// reset timer at end of callback ready for next run
reset();
}, 1000);
As a side note it looks like you've defined two separate flashScreenGreen() and flashScreenRed() functions. I'm guessing that they have similar if not identical logic. If that is the case you might want to consider defining a single utility flashScreen() function which accepts a color as a parameter.
function flashScreen(color) {
// logic utilizing 'color'
}
// usage
flashScreen('green');
flashScreen('#FF0000'); // red as hex

I think the clearTimeout function will help you here
// Hold the reference to the timer
const timeoutId = setTimeout(() => {
if (irregular_tick && space_pressed) {
flashScreenGreen();
//You can use the clearTimeout function to end the timer
clearTimeout(timeoutId);
}
if (!(space_pressed) && irregular_tick) {
flashScreenRed();
//clear timeout, if you need it here too
clearTimeout(timeoutId);
}
},1000);

Related

setTimeout being called multiple times

I can't figure out why setTimeout is being called multiple times in my code.
Here's a snippet of the code with what I thought was irrelevant removed:
let dead;
setup()
{
dead = false;
}
draw()
{
if(fell == true)
{
dead = true;
}
mechanics();
}
function mechanics()
{
let triggerVar;
if(dead == true)
{
triggerVar = 1;
dead = false;
}
if(triggerVar == 1)
{
setTimeout(resetG, 1500);
triggerVar = 0;
}
}
function resetG()
{
lives -= 1;
position = 0;
}
I can't tell what I'm doing wrong because whenever the character dies and setTimeout is called, it is actually not only called after the delay but also for the exact same duration after it is triggered. So in this case it is triggered first after 1500 millis and then every frame for another 1500 millis.
I managed to find the problem, which was not with the code I posted. The problem was that the constructor code that makes the object that changes dead to true if certain conditions are met was being called every frame from the moment it triggered death until the first instance of setTimeout kicked in, which means setTimeout was called every frame for 1500 milliseconds.
Chances are that you mechanics() function is called multiple times, you may give a variable to the settimeout like:
let timeoutID= setTimeout(resetG, 1500);
And in the proper place to clear it, for example after lifecycle
clearTimeout(timeoutID);

Global variable won't change

I'm trying to set up a timer to close a modal on an html page. The problem is that I need a global variable to be used in functions, and I can't re-assign a value to this variable.
I've looked for answers on many sites but none of them worked with my code. The value is correctly set when it is declared, but it can't be re-assigned later when I call it in functions (instead of simply reducing it's value).
No errors are detected in the browser's dev-tools.
var delayToCloseModal = 5;
function AnimateModal() {
// Each time this function is called
delayToCloseModal = 5; // HERE IS THE PROBLEM!!!
// delayToCloseModal is not set at 5 again
// If the modal isn't oppened
if (document.getElementById("modal").style.opacity < 0.01) {
// Open Modal
}
// Check every second if the timer is done, else reduce it by 1
var checkTimer = setInterval(CheckCounterDone, 1000);
function CheckCounterDone() {
if (delayToCloseModal > 0) {
// Reduce timer
delayToCloseModal--; // Strangely, this works really fine!
}
else {
// Stop checking timer
clearInterval(checkTimer);
// Close Modal
}
}
}
The "timer" system works really fine, the modal automatically close 5 seconds after its opening, but if the function is called again when the modal is already open, it won't reset the open timer to 5.
I found a solution for the problem! (It might be "homemade", but it works)
I created a Boolean variable to enable or disable the creation of a setInterval if one is already running. Here is the code :
var delayToCloseModal = 5;
var timerRunning = false; // Set false as default value to allow the creation of the first timer
function AnimateModal() {
// Each time this function is called
delayToCloseModal = 5;
// If the modal isn't oppened
if (document.getElementById("modal").style.opacity < 0.01) {
// Open Modal
}
// Check if a timer is already running
if (!timerRunning){
var checkTimer = setInterval(CheckCounterDone, 1000); // Create new timer
timerRunning = true; // Forbids to create another new timer
}
function CheckCounterDone() {
if (delayToCloseModal > 0) {
// Reduce timer
delayToCloseModal--;
}
else {
// Stop checking timer
clearInterval(checkTimer); // Delete the existing timer
timerRunning = false; // Allow to recreate another timer
// Close Modal
}
}
}
The modal now stays opens for 5 seconds since the last call of the function, and the timer is only reduced by one each second, even if you spam the call of the function, and close succesfully after the delay has been set to 0.
Thanks to anyone who has answered for your help! :)

Countdown timer increase on user interaction

Consider this C# question: Countdown timer increase on interaction?
I am in need of an equivalent for Javascript. That is, the following:
Problem
I need a callback to be called after some time T however if user interaction happens before the callback is executed then this time T must be increased by some number X. How is this modelled using Javascript?
Background (the why)
I have a page forward button which upon being clicked increases the page by 1. Increasing the page causes some hefty computations to happen so it'd be preferable to only switch page after some small time frame so that a user that's spamming the page button won't make the programs perf. go in the bin.
If I understand correctly, you can simply clear the timeout and set it again.
clearTimeout(timer);
timer = setTimeout(function(){}, 1000);
I made a quick codepen with an example. I hope it helps: https://codepen.io/daniti/pen/gjePdo
You can use a debounce function. It sets a timer and if an interation happens before timer expiration it deletes the old timer and creates a new one, effectively resets the time. Example:
function debounce(fn, delay) {
let timerId;
return function (...args) {
if (timerId)
clearTimeout(timerId);
timerId = setTimeout(() => {
fn(...args);
timerId = null;
}, delay);
}
}
You can use a timeout : https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
document.getElementById("spamMe").onclick = doLongAction;
var canDoLongAction = true;
var timeout;
function doLongAction() {
if (canDoLongAction) {
// prevent relaunch of action
canDoLongAction = false
// the action will become available in 2sec
timeout = window.setTimeout(
() => {canDoLongAction = true},
2000
)
// you do your long action
alert("hey")
} else {
// if clicked while the timeout is going reset it
window.clearTimeout(timeout);
timeout = window.setTimeout(
() => {canDoLongAction = true},
2000
)
}
}
<button id="spamMe">spam me!!</button>
in the example, the button is blocked until you stop clicking for 2 seconds

How to remove the queue working of settimeOut?

After clearTimeout(tt);, tt = setTimeout(function () { is stopped to work. Why? I have the Recursion and when i execute getByClients in manually i need to remove current queue of setTimeout(if exists) and execute new setTimeout, instead after clearTimeout(tt); i am getting nothing.
var tt = undefined;
// in default select = null, only by btn click='getByClients(1)'
function getByClients(select) {
if (select == 1 || select == 2) {
clearTimeout(tt); // after, setTimeout is not executed
}
dashboardService.getByClients($scope.clientsMode).then(function(response) {
tt = setTimeout(function() {
getByClients(null);
}, refreshTime);
});
};
HELP, someone, please (
I don't see the need of recursive code. Perhaps your sample is too simplified ?
If I understand correctly, you need to automatically call a service every X seconds, but you want to be able to force immediate call.
In this case here is how I would do that :
var tt = null;
// - Stop current timer (is started)
function stopTimeout()
{
if(tt)
{
clearTimeout(tt);
tt = null;
}
}
// - Start a new Timer (and stops the previous one if any)
function startTimeout()
{
stopTimeout(); // - to clean if already started (security)
tt = setTimeout(function() {
getByClients();
}, refreshTime);
}
function getByClients()
{
// - Stop automatic Timer
stopTimeout();
// - Executes Query
dashboardService.getByClients($scope.clientsMode).then(function(response) {
// - "Read response "
// - Restart timer
startTimeout();
}
);
}
// - Start automatic calls
getByClients();
There is no need in this case for a parameter : each call stops current timeOut.
I think a better way could be to use a setInterval, but I wanted to be near your original code.
My example code restarts automatic updates after a manual one. I don't know if this is your need ?
EDIT : I see you use AngularJS, perhaps $timeout would better suit your needs ?

setTimeout function associated with a event

i want to be able to call my function of setTimeout when a user enter a number in my input after 1s.
But the problem i want that this event is called just once for example when a user enter 3 numbers like 200 in a time less than 1s.
My code below is called 3 time if a user enter 3 numbers like 200
$('input.amount').on('input.amount',function(e){
setTimeout(function() {
// code
}, 1000);
});
So i want that this code is called just once
You may be able to keep a variable that tells you if the user already typed a character. Then only trigger the function if the variable is not yet set. For example:
var started = false;
$('input.amount').on('input',function(e){
if(!started){
started = true;
setTimeout(function() {
// code
started = false;//to reset so that further typing will trigger again
}, 1000);
}
});
One approach I would take is to delay the execution till after 1000ms after the last input
var timer;
$('input.amount').on('input.amount', function (e) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function () {
// code
timer = undefined;
}, 1000);
});

Categories