Function / setInterval unexpectedly logging twice to the console - javascript

I'm trying to create a callback to my Web API to collect new race data, I've created a setInterval and tested it to see if it does log to the console once as I would expect but for some reason the console is logging twice. I don't understand why.
I am wanting to remove the console.log() inside the if statement and replace that with a call to the server but if it's logging twice i'll receive twice as much information.
var TimeOutID = '';
var TenRaces = 0;
var StartThis = function () {
console.log("Starting run");
//Simulate counting down to next race
TimeOut = new Date().getTime() + 5000;
$('#next').countdown(TimeOut, function (event) {
$(this).html(event.strftime('%H:%M:%S'));
if(event.elapsed) {
//once the countdown timer has elapsed fetch data
console.log("Race: " + TenRaces); // <-- this one is logged twice
}
});
//loop ten races simulating ten different times data is needed
TenRaces++;
if (TenRaces > 10) {
clearInterval(TimeOutID);
}
};
TimeOutID = setInterval(StartThis, 6000);
Why is console.log() running and logging twice?
I'm using the jQuery plugin from http://hilios.github.io/jQuery.countdown/

According to the documentation, the elapsed mode is deliberately designed to continue after the countdown has finished. Therefore I think it's looping twice before the TenRaces var iterates. It would just keep logging forever if you didn't iterate that var.
Do the log/fetch data on the finish.countdown event instead, this triggers just once, when the countdown finishes. See documentation:
countdown documentation
E.g.
$('div#clock').countdown(finalDate)
.on('finish.countdown', callback);

Related

How to change instanly a DOM element?

I know that this might be a stupid question but it drives me crazy. I'm trying to change the innerHTML of a DOM element but it doesn't change until the end of the function's execution. For example:
function test(){
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; i++)
{
testEl.innerHTML = 'Count: ' + i;
alert(i);
}
}
Even if I have put an alert in the loop, the text of the element will not change until the end of the function's execution. How can the change be applied instantly (for example I mean during the loop)?
You can update the number every period of time using setInterval:
function test(){
let testEl = document.getElementById('testEl');
let i = 0;
const interval = setInterval(function(){
testEl.innerHTML = `Count: ${i++}`;
if(i === 5)
clearInterval(interval);
}, 1000);
}
test();
<p id="testEl"></p>
JavaScript runs in a single-threaded environment. This means that only one execution context can ever be running at any single point in time. Asynchronous code executes outside of the JavaScript runtime environment (in this case by the browser's native processing) and only when the JavaScript thread is idle can the results of an asynchronous request be executed (i.e. callbacks).
Below is an example that updates a DOM element approximately every second, creating a clock. However, if you click the button, it will ask the browser to render an alert, which is handled outside of the JavaScript runtime and is a blocking UI element, so the clock will stop. Once you clear the alert, you will see the time jump to be roughly current.
As you'll see, the asynchronous API call to window.setInterval() allows for the function to run repeatedly, every so often, and therefore not continuously. This replaces the need for a loop that runs in its entirety every time its accessed. Because of this, you can see updates to the webpage instead of the last value of your loop.
See the comments for more details:
const clock = document.querySelector("span");
// setInterval is not JavaScript. It's a call to a browser
// API asking the JS runtime to run the supplied function every
// 900 milliseconds, but that's just a request. After 900
// milliseconds, the browser will place the function on the
// JavaScript event queue and only when the JavaScript thread
// is idle will anything on the queue be executed. This is why
// the 900 milliseconds is not a guarantee - - it's just the
// minimum amount of time you'll have to wait for the function
// to run, but it could be longer if what's already running
// on the JavaScript thread takes longer than 900 milliseconds
// to complete.
window.setInterval(function(){
// Update the DOM
clock.textContent = new Date().toLocaleTimeString();
}, 900);
document.querySelector("button").addEventListener("click", function(){
// An alert is also not JavaScript, but another browser API that is executed
// by the browser, not JavaScript. However, it is a blocking (modal) UI element.
// The rest of the browser interface (including the web page) cannot update
// while the alert is present. As soon as the alert is cleared, the UI will update.
window.alert("I'm a UI blocking construct rendered by the browser, not JavaScript");
});
<div>Current time is: <span></span></div>
<button>Click for alert</button>
Another way to achieve it is by using async - Promise like this
async function test() {
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; ++i) {
testEl.innerHTML = 'Count: ' + i;
await new Promise((resolve, _) => {
setTimeout(function() {
resolve("");
}, 1000 /* your preferred delay here */);
});
}
}

How do you debounce a function after the first call?

I am trying to make a function similar to debounce, except I want the debounced function to be called immediately after the first call and all calls after the first call within the debounce time limit to be debounced. For example, if the function is hooked up to a button, and you rapidly and repeatedly click it 10 times, the function gets called on the first press and gets called a second time after x milliseconds have passed since the tenth press.
Pure JavaScript processing:
Requiring a repeated call within the "debounce" period to be called in addition to the first call adds a complication that can be addressed using a timer in addition to timestamps to make immediate calls if possible.
However, because calls to a function may be delayed, it is not always possible to return a value from the function to the caller in real time.
The following concept code
calls the process "lock" in the sense of locking out calls in a predefined way.
returns undefined to all calls without implementing call backs to allow a caller to tell if its call was actioned or not, or to retrieve a return value;
When a call is made after a lockout period within which addition call attempts were made, the actual arguments used are to place a delayed call are those supplied by the most recent call attempt.
function CallLock( toCall, lockout) {
let argv;
let lastCall = 0;
let timer = 0;
function recall() {
timer = 0;
lastCall = Date.now();
toCall(...argv);
}
return function( ...args) {
let now = Date.now();
if(timer == 0) {
if( now >= lastCall+lockout) {
lastCall = now;
toCall( ...args);
}
else {
argv = args;
timer = setTimeout(recall, lastCall+lockout - now);
}
}
else {
argv = args; // use most recent arguments
}
}
}
// test CallLock
let start;
function demo( msg) {
console.log( "demo('%s') called. Time is %sms after start", msg, Date.now() - start);
}
let lockDemo = CallLock( demo, 1000); // 1 second lockout
start = Date.now();
lockDemo("call 1");
setTimeout( lockDemo, 200, "call 2");
setTimeout( lockDemo, 400, "call 3");
setTimeout( lockDemo, 1800, "call 4");
Test code uses a 1 second lockout time. Note that timer delays are inexact and Date.now() rounds to the nearest millisecond. The expected results are
call 1 is made synchronously and will show a start time of 0 or 1ms.
call 2 is never actioned - its arguments are not used.
call 3 is actioned, but delayed until shortly after the first call lockout
call 4 is actioned, but also delayed because the lockout period from when call 3 was actioned is still in effect.
Sounds like throttle. check this article for difference between throttling and debouncing. if throttle isn't what you need, then you should implement what you need from scratch (and adding more explanation for the details).
Edit:
So, Yes, It is not throttle; It is debounce with invocation on leading edge in lodash;
_.debounce(yourCallback, 100, {
'leading': true
})
I would suggest don't go for debounce. The older debouncing technique relies on setTimeout which is not perfect. Instead try to make use requestAnimationFrame which has in built support for the next trigger for Dom visual states change.

Wrong use of Javascript setInterval()

I have a function called using setInterval of JavaScript, which in some scenarios is called multiple times without the interval gap defined (I suspect this is because the intervals are not cleared properly and I'm creating multiple intervals, but I'm not sure).
I can not reproduce the problem locally.
The code uses Twirl but it's basically JS:
function refreshCheckInRequests() {
if (interval) { // If there is an interval running stop it.
clearInterval(interval);
}
jsRoutes.controllers.ExtranetSecuredController.findPendingCheckInRequests("#gymId").ajax({ // Ajax call using Play Framework
success: function (data) {
$("#checkin-request-container").html(data);
addRowListeners()
},
error: function (data) {
if (data.status == 401) {
errorSwitchGym("#Messages("extranet.switch.gym")");
//location.reload();
}
else {
unexpectedError(data)
}
},
complete: function() {
interval = initInterval(); // At the end of the call init the interval again
}
});
}
function initInterval() {
return setInterval(function () { refreshCheckInRequests(); },
20000);
}
var interval;
refreshCheckInRequests();
$("#checkin-request-refresh").click(function (event) {
refreshCheckInRequests();
event.preventDefault();
});
I could use setTimeout instead because at the end, I always call refreshCheckInRequests once, I stop the interval, and at the end I create a new one.
If I use timeout I have to call again my function at the end of the execution of the callback of timeout (like I'm doing right now). If something goes wrong, my callback will never be called again.
Anyway, I would like to know what's going on here. Am I missing something? Am I doing something wrong? Any suggestions?
You're clearing the current interval every time refreshCheckInRequests is called, but there is a delay between when refreshCheckInRequests is called and the new interval is assigned. Because refreshCheckInRequests also runs when an element is clicked, the following scenario could result in an unterminated interval:
User clicks, current interval is cleared, asynchronous findPendingCheckInRequests runs
User clicks again, no interval currently exists (nothing to clear), another asynchronous findPendingCheckInRequests runs
Response from first findPendingCheckInRequests comes back. complete handler runs, interval is assigned to the new interval
Response from second findPendingCheckInRequests comes back. complete handler runs, interval is assigned to the new interval over the old interval
The first created interval remains running, but there no longer exists a reference to it, so that first interval continues repeating forever.
So, try clearing the interval at the moment you reassign interval, ensuring that every new interval will always clear the old one, if an old one is running:
complete: function() {
clearInterval(interval);
interval = initInterval();
}

How setInterval and setTimeout behave on Phonegap/Cordova when app resumes from background? [duplicate]

I have a jQuery Mobile web app which targets iOS and Android devices. A component of the application is a background task, which periodically checks for a.) changes to local data and b.) connectivity to the server. If both are true, the task pushes the changes.
I'm using a simple setTimeout()-based function to execute this task. Each failure or success condition calls setTimeout() on the background task, ensuring that it runs on 30 second intervals. I update a status div with the timestamp of the last task runtime for debugging purposes.
In any desktop browser, this works just fine; however, on iOS or Android, after some period of time, the task stops executing. I'm wondering if this is related to the power conservation settings of the devices--when iOS enters stand-by, does it terminate JavaScript execution? That is what appears to happen.
If so, what is the best way to resume? Is there an on-wake event which I can hook into? If not, what other options are there which don't involve hooking into events dependent on user interaction (I don't want to bind the entire page to a click event just to restart the background task).
Looks like Javascript execution is paused on MobileSafari when the browser page isn't focused. It also seems if setInterval() events are late, they are simply fired as soon as the browser is focused. This means we should be able to keep a setInterval() running, and assume the browser lost/regained focus if the setInterval function took much longer than usual.
This code alerts after switching back from a browser tab, after switching back from another app, and after resuming from sleep. If you set your threshold a bit longer than your setTimeout(), you can assume your timeout wouldn't finish if this fires.
If you wanted to stay on the safe side: you could save your timeout ID (returned by setTimeout) and set this to a shorter threshold than your timeout, then run clearTimeout() and setTimeout() again if this fires.
<script type="text/javascript">
var lastCheck = 0;
function sleepCheck() {
var now = new Date().getTime();
var diff = now - lastCheck;
if (diff > 3000) {
alert('took ' + diff + 'ms');
}
lastCheck = now;
}
window.onload = function() {
lastCheck = new Date().getTime();
setInterval(sleepCheck, 1000);
}
</script>
Edit: It appears this can sometimes trigger more than once in a row on resume, so you'd need to handle that somehow. (After letting my android browser sleep all night, it woke up to two alert()s. I bet Javascript got resumed at some arbitrary time before fully sleeping.)
I tested on Android 2.2 and the latest iOS - they both alert as soon as you resume from sleep.
When the user switches to another app or the screen sleeps, timers seem to pause until the user switches back to the app (or when the screen awakens).
Phonegap has a resume event you can listen to instead of polling for state (as well as a pause event if you want to do things before it is out of focus). You start listening to it after deviceReady fires.
document.addEventListener("deviceready", function () {
// do something when the app awakens
document.addEventListener('resume', function () {
// re-create a timer.
// ...
}, false);
}, false);
I use angular with phonegap and I have a service implemented that manages a certain timeout for me but basically you could create an object that sets the timer, cancels the timer and most importantly, updates the timer (update is what is called during the 'resume' event).
In angular I have a scopes and root scope that I can attach data to, my timeout is global so I attach it to root scope but for the purpose of this example, I'll simply attach it to the document object. I don't condone that because you need should apply it to some sort of scope or namespace.
var timeoutManager = function () {
return {
setTimer: function (expiresMsecs) {
document.timerData = {
timerId: setTimeout(function () {
timeoutCallback();
},
expiresMsecs),
totalDurationMsecs: expiresMsecs,
expirationDate: new Date(Date.now() += expiresMsecs)
};
},
updateTimer: function () {
if (document.timerData) {
//
// Calculate the msecs remaining so it can be used to set a new timer.
//
var timerMsecs = document.timerData.expirationDate - new Date();
//
// Kill the previous timer because a new one needs to be set or the callback
// needs to be fired.
//
this.cancelTimer();
if (timerMsecs > 0) {
this.setTimer(timerMsecs);
} else {
timeoutCallback();
}
}
},
cancelTimer: function () {
if (document.timerData && document.timerData.timerId) {
clearTimeout(document.timerData.timerId);
document.timerData = null;
}
}
};
};
You could have the manager function take a millisecond parameter instead of passing it into set, but again this is modeled somewhat after the angular service I wrote. The operations should be clear and concise enough to do something with them and add them to your own app.
var timeoutCallback = function () { console.log('timer fired!'); };
var manager = timeoutManager();
manager.setTimer(20000);
You will want to update the timer once you get the resume event in your event listener, like so:
// do something when the app awakens
document.addEventListener('resume', function () {
var manager = timeoutManager();
manager.updateTimer();
}, false);
The timeout manager also has cancelTimer() which can be used to kill the timer at any time.
You can use this class github.com/mustafah/background-timer based on #jlafay answer , where you can use as follow:
coffeescript
timer = new BackgroundTimer 10 * 1000, ->
# This callback will be called after 10 seconds
console.log 'finished'
timer.enableTicking 1000, (remaining) ->
# This callback will get called every second (1000 millisecond) till the timer ends
console.log remaining
timer.start()
javascript
timer = new BackgroundTimer(10 * 1000, function() {
// This callback will be called after 10 seconds
console.log("finished");
});
timer.enableTicking(1000, function(remaining) {
// This callback will get called every second (1000 millisecond) till the timer ends
console.log(remaining);
});
timer.start();
Hope it helps, Thank you ...
You should use the Page Visibility API (MDN) which is supported just about everywhere. It can detect if a page or tab has become visible again and you can then resume your timeouts or carry out some actions.

javascript variable use outside function

$scope.pagination_data = function(page_data){
count = page_data.data.count;
};
console.log("outside count is",count);// this not working
var page = function(){
userService.paginate()
.then(function(user){
$scope.pagination_data(user);
console.log("count is",count);//this is works
});
};
in that code i m using $http service by using that i m getting data aftr that i want a count then i m getting count also but now i want this count variable access in controller but this is not accessible to me. what should i do..?
Actually, both works but you're calling the first one at the wrong time.
The .then() indicates that paginate() returns a promise. Which should be a hint that it is asynchronous.
To prove this, delay calling your first console.log by using setTimeout:
$scope.pagination_data = function(page_data){
count = page_data.data.count;
};
setTimeout(function(){
console.log("outside count is",count);// this should work
}, 5000); // wait 5 seconds before calling the code above
// If 5 seconds is not long enough increase it to 10 or something
var page = function(){
userService.paginate()
.then(function(user){
$scope.pagination_data(user);
console.log("count is",count);//this is works
});
};
What is asynchronous code? Asynchronous simply means that the code will be called later. What paginate().then() does is not call the function(user) function immediately but rather remembers that it is supposed to call function(user) (which then calls your pagination_data function to set the value of count) at a later time. It then continues on running other stuff.
When there's no other code to run, the event loop gets processed and whatever userService.paginate() needs to do asynchronously gets processed.
When whatever userService.paginate() is waiting for finally returns (this may be a network request, user click etc) the function(user) function finally gets called. This in turn calls $scope.pagination_data() which is what assigns the result to the global count variable.
What does that setTimeout() do? Well, it does the same thing I describe above. Instead of calling console.log() immediately it remembers to call it later. Then when 5 seconds (5000 milliseconds) expires, and there is no other javascript code running (this is important because it means javascript can run the event loop), the console.log() finally gets called.
Just define the variable outside.
var count = 0;
$scope.pagination_data = function(page_data){
count = page_data.data.count;
};
console.log("outside count is",count);// this not working
var page = function(){
userService.paginate()
.then(function(user){
$scope.pagination_data(user);
console.log("count is",count);//this is works
});
};
The fist statement only defines the function, it doesn't call it. If you have data to pass to that function call it before logging the count.
// this only defines the function, doesn't call it
$scope.pagination_data = function(page_data){
count = page_data.data.count;
};
console.log("outside count is",count);// count is still unset
var page = function(){
userService.paginate()
.then(function(user){
$scope.pagination_data(user); // this is the call, count will be set
console.log("count is",count);//this will work
});
};

Categories