I've got a countdown on my website done with setTimeout() but I realize that with Internet Explorer 11 (and older I guess) when the user right clicks anywhere on the browser, the countdown just stops because the "context menu" from the browser is open. The countdown starts back when the context menu is close.
The solution of blocking the rightclick during this countdown is not an usable solution in my case.
I've also check the requestAnimationFrame() method, but if the user minimize the browser, the countdown also stops.
The countdown is not lasting forever, it can last from 3 minutes to 10 seconds.
Any workaround/ideas ?
Thanks
You can't rely on setTimeout or setInterval to track time. They will approximately run when they are meant to but it is dependent on what else is taking up processing time and some blocking events like you have discovered. You should think of them as a way to update your clock but maintain the clock state as a differential between the time you started and the time your timer function fires. Because the timing of when the function fires isn't exact I try to set the interval lower than what I want. If I want it to tick every second then I set the interval to a half or quarter second to ensure that I make up for misses quicker.
Here is a rough example:
function countdown(duration) {
const start = Date.now();
const interval = window.setInterval(function() {
const remaining = duration - (Date.now() - start);
if (remaining <= 0) {
console.log(0);
window.clearInterval(interval);
return;
}
console.log(remaining);
}, 500);
}
countdown(5000);
Related
I am currently trying to get a repeating sound effect, which is getting slower over time with setTimeout() in sync with an animation. As soon as I get it in sync it will work and stay in sync for the time I am working on the program. But now when I was away for about 1 1/2 hours and run the program again exactly as I left it, the sound is no longer in sync with the animation. The same thing happend to me with the same program yesterday when I was away for some time and overnight.
So I was thinking that setTimeout() is somehow working with the current time and will work differently at different times. Can someone confirm this?
Here is my code for reference.
The timeout function:
const timeoutRollSound = (time = 0, index = 0) => {
setTimeout(() => {
const audioClick = new Audio(
"foo/bar.wav"
);
audioClick.play();
index++;
timeoutRollSound(0.05 * Math.pow(index, 2) + 3 * index - 50, index)
}, time);
};
The animation:
$(".itemToAnimate").animate(
{ right: endpoint },
{
duration: 10000,
easing: "easeOutQuint",
}
);
I had this issue in Java years ago. Here's what's going on.
When you set a timeout (like you are doing) you are actually saying "I don't want this function to execute before X milliseconds". So the timeout function may be ready to run, but JavaScript or the browser might be doing something else.
setInterval might work better. But the other thing you can do is include the difference between when the code was eligible to be run and the time it was actually run at, like:
setTimeout(() => {
const audioClick = new Audio(
"foo/bar.wav"
);
audioClick.play();
index++;
timeoutRollSound(0.05 * Math.pow(index, 2) + 3 * index - 50, index)
timeoutRollSound.last = Date.now();
}, time - ((Date.now - timeoutRollSound.last) );
This reminds me of an issue I was having with another JS library and could be related. If you put the tab in browser to the background, the execution will be suspended. From what I'm getting from your code, you rely on the fact that the recursion setTimeout will run constantly which could be the source of your issues.
This could be the issue you are having, take a look: Chrome: timeouts/interval suspended in background tabs?
Background
Suppose I have a metaphorical patient whose heart beats once per second, and every second, I check whether his last heartbeat was more than five seconds late (and, if so, declare him to be in danger):
let lastHeartbeat = Date.now();
// Heartbeater
setInterval(() => lastHeartbeat = Date.now(), 1000);
// Health-checker
setInterval(
() => {
if((Date.now() - lastHeartbeat) > 5000){
alert("Patient has flatlined");
}
},
1000
);
Issue
If I use a debugger to pause the execution of this script at some point, I have a problem: if I remain on a breakpoint for more than five seconds, then, once script execution resumes, my health-checker function is certain to declare that the patient has flatlined.
Desired behaviour
Instead, I'd like to factor in the time spent in the debugger.
i.e., if I spend twenty seconds sitting on a breakpoint just after an initial heartbeat occurring, and the patient's heart beats again within just one second of releasing that breakpoint, then that patient should not be declared as flatlining.
Is there any way to subtract the time spent in the debugger from the health-check condition? e.g.:
if((Date.now() - lastHeartbeat - lastTimeSpentInDebugger) > 5000)
Note: I'm specifically running the JS in Node.js, rather than in a browser.
I write extension for Chrome. And I need run delayed tasks when background page inactive. Cause setTimeout not working in background tabs, I try emulate setTimeout with setInterval, like code below (located in content script):
window.timings = [];
function set_timeout(func, time){
var now = new Date() / 1;
window.timings.push({
func: func,
time: time + now
});
}
function tick(){
var now = new Date() / 1;
window.timings = window.timings.filter(function(delay_obj){
if (now > delay_obj.time){
delay_obj.func.call();
return false;
} else {
return true;
}
});
}
$(function() {
setInterval(tick, 1000);
// some code
});
And it don't work when set_interval call in delay function:
set_timeout(function(){
console.log('func1');
}, 2000);
set_timeout(function(){
console.log('func2');
set_timeout(function(){
console.log('func3');
}, 3000);
}, 3000);
Output:
func1
func2
Why func3 not displayed?
You're apparently using an event page declared with "persistent": false in manifest.json, it is unloaded after 15 seconds of inactivity. The linked documentation says to use chrome.alarms API.
For a delay less than 15 seconds since the last chrome event:
Use setTimeout or setInterval.
For a delay of 15-60 seconds since the last chrome event:
Don't use the event page, switch to "persistent": true in manifest.json.
For a delay of 60 seconds or more:
manifest.json:
"permissions": ["alarms"],
background script:
chrome.alarms.create("MyInterval1", {when: Date.now() + 1 * 60e3});
chrome.alarms.onAlarm.addListener(function(alarm) {
if (alarm.name == "MyInterval1") {
console.log("Yay!");
chrome.alarms.create("MyInterval1", {when: Date.now() + 1 * 60e3});
}
});
Also note:
Other asynchronous HTML5 APIs like notifications and geolocation will not complete if the event page shuts down. Instead, use equivalent extension APIs, like notifications.
If your extension uses, extension.getBackgroundPage, switch to runtime.getBackgroundPage instead. The newer method is asynchronous so that it can start the event page if necessary before returning it.
Note that in a published extension the interval between the next alarm and the last fired alarm is at least 1 minute even if you specify a smaller value like 15 seconds (15*1000).
Source: https://developer.chrome.com/docs/extensions/reference/alarms/#method-create
In order to reduce the load on the user's machine, Chrome limits
alarms to at most once every 1 minute but may delay them an arbitrary
amount more. That is, setting delayInMinutes or periodInMinutes to
less than 1 will not be honored and will cause a warning. when can
be set to less than 1 minute after "now" without warning but won't
actually cause the alarm to fire for at least 1 minute.
To help you debug your app or extension, when you've loaded it
unpacked, there's no limit to how often the alarm can fire.
So I'm attempting to make a Pomodoro Timer without using an API (I know, stupid choice) but I feel as if I'm over-complicating this issue.
I forked my CodePen so I could post the current code here without confusing anyone. My Code Pen
To see my issue: Just set Timer to .1 and Break to .1 - You'll see the Start to Resume works fine, but the Resume to start has issues.
I built in consoleLogs to track it and I see the Work Timer TRIES to start but then breakTimer over-runs it, and duplicates on every pass.
Why isn't my clearInterval working?
Things I've tried:
Adjusting names of clearInterval,
Setting it so it goes back to startTimer instead of start
force quitting it (instead of looping it back to startInterval.
The function is virtually identical to my startFunction yet fails to work properly. Would appreciate any input (I'm new to clearInterval but I believe I am using it right.)
function breakTimer() {
$('.jumbotron').css('visibility', 'visible');
setInterval(function() {
console.log("Break Timer...");
breakTime--;
if (breakTime < 0) {
clearInterval(timer);
working = false;
start();
} else {
showTime(breakTime);
}
}, 1000);
}
Edit:
To answer the reply:
function start() {
if (working == true){ //This keeps it from being spammable
return;
} //Else
workTime = $('#work').val()*60;
breakTime = $('#break').val()*60;
working = true;
checkStatus();
timer = startTimer();
}
Unsure if I should post every Function here
As per definition, the value returned by setInterval(...) is the ID of the created timer. As such, with your code you can only stop the last created timer because the ID in the timer variable gets overwritten, causing it to lose control over the previously created (and still running) timers.
The ID is what you pass on to clearInterval(...) to stop a timer. You will have to do this in a different way. You may ask for a different way in https://codereview.stackexchange.com/
It seems that everyone has a few problems with clearInterval. I have built a slider that allows people to hover a click on arrows. The banner also rotates ever few seconds. I want to be able to have the auto-rotate turn off after someone clicks on one of the arrows.
Here's my code:
$(function(){
var intvl = 0;
intvl = setInterval(heroTransitionNext, 2000);
$('.rightArrow').click(function(){
window.clearInterval(intvl);
});
});
EDIT:
Here is the function it is calling:
function heroTransitionNext() {
$('.HP-hero li').filter(':visible').fadeOut('normal', function () {
if ($(this).next().length != 0) {
activeZone = parseInt(activeZone) + 1;
$(this).next().fadeIn('normal', heroNavHighlight(activeZone));
} else {
activeZone = 1;
$('.HP-hero li:first-child').fadeIn('normal', heroNavHighlight(activeZone));
}
$(this).hide();
});
};
To stop the animation you can use jquery's .stop() but not sure whether it'll solve the problem or not that you are facing (didn't visualize) but you can give it a try
$('.HP-hero li').stop(1,1); // or try $('.HP-hero li').stop()
window.clearInterval(intvl);
As say2joe said that clearInterval will just stop the function from invoking next time but it won't clear the current queue (he is right) so in that case stop could be used.
About Stop.
Depending on how much work your heroTransitionNext function is doing, it may still be executing even though the interval is cleared -- in other words, clearing the interval will stop the function from being invoked -- but, any instance of the function(s) executing in memory will continue to execute until finished.
To be more clear, here's a use case (you can check this out yourself by using a profiler in Firebug or Developer Tools):
heroTransitionNext execution time is 2.1 seconds.
clearInterval is invoked 6.1 seconds after setInterval is invoked.
At 6.1 seconds, heroTransitionNext has been invoked four times. The first three executions have completed, however, the fourth will not complete until it finishes executing (at 8.1 seconds since setInterval was called). Note: In this use case, each successive invokation will execute while the last invokation's execution is still continuing (for 100 more ms) -- in other words, you'll have execution overlap from 2 to 2.1, 4 to 4.1, and 6 to 6.1 second intervals.
If the function takes longer to execute than the interval set, use a recursive function with setTimeout(). The following link will give you a good example.
Also, a good reference for explanation is https://developer.mozilla.org/en/DOM/window.setInterval.