Anomaly in setInterval function in asynchronous functions calls in Javascript - javascript

Below is the snippet of the code I am preparing to build a website.
<script type="text/javaScript">
function h(d) {
console.log("hello");
return;
};
function func(callback) {
var httpRequest; // create our XMLHttpRequest object
if (window.XMLHttpRequest) {
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) {
// Internet Explorer old versions
httpRequest = new
ActiveXObject("Microsoft.XMLHTTP");
}
httpRequest.onreadystatechange = function() {
// inline function to check the status
if (httpRequest.readyState === 4 &&
httpRequest.status === 200) {
callback(httpRequest.responseText);
// call the callback function
}
};
httpRequest.open("GET", '/try_ajax',true);
httpRequest.send();
}
// call the function
setInterval(func(h), 10000); // Timer is triggered here
</script>
Interestingly, though I have set the interval at 10 seconds, in my console the "hello" is appearing only once. But it should keep appearing after 10 seconds. Please help!

this is wrong
setInterval(func(h), 10000); // Timer is triggered here
You are saying take whatever is returned from func(h) and assign it to the timeout
setInterval(function(){func(h);}, 10000); // Timer is triggered here

You are actually calling the function in your setInterval call, which is not what you want. You should only give a reference to it and put parameters as the last arguments, like this:
setInterval(func, 10000, h); // Timer is triggered here
I'd like to give a shoutout to some who pointed out that the OP wanted additional parameters with his callback, something that I completely missed in my initial answer.

Fact of the matter is - when you do
setInterval(func(h), 10000)
return value of func(h) is called every 10 seconds. Your func(h) returns void. Therefore in effect after the first call, there is nothing to call for the setInterval function. Therefore you see the first invokation and nothing after that!

Related

Function called on interval accesses global variable

I am asynchronously receiving data (from 0 to 100 points per second) that is passed to a high-startup-cost function, extendTraces(data), which updates the user interface. If I call extendTraces() upon generating every point, the user interface becomes unresponsive. I've found that it is much more efficient to call the function periodically and pass it an array of points, call it pointArray.
I can track how many points have been added to pointArray and call extendTraces(pointArray) every 20th addition:
//inside asynchronous function
pointArray.push(point);
if (this.pointArray.length == 20){
(<any>Plotly).extendTraces(this.pointArray);
this.resetPointArray();
}
But if I fill pointArray halfway and don't receive any data for a while, I'd also to call extendTraces.
My solution is to call extendTraces() every second
//inside a function that is called when the page loads
window.setInterval(function() {
if (pointArray.length > 0){
(<any>Plotly).extendTraces(this.pointArray);
this.resetPointArray();
}
}, 1000);
My function that receives the points will simply concatenate them onto the pointArray.
//inside asynchronous function
pointArray.push(point);
I am new to js and was wondering if I'm using the correct paradigms for this task. I see a lot of information about callbacks and promises which I don't fully understand but I am suspicious that I'm doing something wrong by not using them. Coming from c++, I am concerned that two functions, the function defined in setInterval and the asynchronous function that receives points, both have access to the pointArray without any hardcoded mutex.
I would be tempted to wrap the logic into its own class, where it simply allows you to specify
After how many adds to call the method
After how long of inactivity to call the method
the method to call
function PointHandler(flushSize, flushTime, flushCallback){
var pointArray = [];
var lastFlush = setTimeout(() => this.flush(),flushTime)
this.addPoint = function(point){
pointArray.push(point);
if(pointArray.length == flushSize){
this.flush();
}
clearTimeout(lastFlush)
lastFlush = setTimeout(() => this.flush(), flushTime);
}
this.flush = function(){
flushCallback(pointArray);
pointArray = [];
clearTimeout(lastFlush)
lastFlush = setTimeout(() => this.flush(), flushTime);
}
}
var handler = new PointHandler(10, 5000, points => console.log(points));
document.getElementById("clickme").addEventListener("click", () => handler.addPoint(new Date()));
<button id="clickme">Add point</button>
The above code will call the callback after 5 seconds of inactivity, or when it gets 10 points added.
My callback simply console.log the current points, but you could call your method.
You should create a debounce function, this basically limits how often the function can be called. Here is the debounce function from the underscore library:
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, result;
var later = function(context, args) {
timeout = null;
if (args) result = func.apply(context, args);
};
var debounced = restArguments(function(args) {
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
timeout = _.delay(later, wait, this, args);
}
return result;
});
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
};
return debounced;
};
Now just wrap your extendTraces function around the debounce function and then call the function that is return from it.
Source: https://github.com/jashkenas/underscore/blob/master/underscore.js#L887-L914

JS - Set delay between sending AJAX request

I am working on building a search feature into my site. To do this I have a search bar which sends an AJAX request to my server each time a user types something. The server will in turn send back the items which match the search.
The only problem with this currently is that if a user types "a" and then "b" what it will send is:
a
ab
To counter this I have found setTimeout which delays when the user enters a search; however, currently it is only delaying when the strings fire (i.e. waits 0.75 seconds before sending a and then waits 0.75 seconds before sending ab).
Here's the JS:
$('#searchBox').keyup(function(e) {
var timeoutID = setTimeout(searchRequest, 750, $(e.currentTarget).val());
});
function searchRequest(str) {
if (str.length > 0) {
console.log('search request initalized | sending: ', str);
var xhttp = new XMLHttpRequest();
xhttp.open('POST', 'code to send here', true);
xhttp.send(str);
}
}
I think what you need is a debounce function.
Here's the basic JavaScript debounce function (as taken from Underscore.js):
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
You can use it to debounce click:
$('#searchBox').keyup(debounce(function(e) {
searchRequest$(e.currentTarget).val());
}, 750));
You have to clear thew timeout to make it work. Have a look on this link ;) Resetting a setTimeout
Basically, when the user added a letter, you check if you already defined a timeout. If you defined a timeout you clear it. Then you reset the timeout. Something like that
var yourTimeout;
function sendInfo(info){
if (yourTimeout != undefined)
clearTimeout(yourTimeout);
yourTimeout = setTimeout(function(){
//do something
}, 500);
}
In your case, the sendInfo function is the keyup handler, and you call searchRequest in the timeout like you already did ;)
Hope it helps

SetTimeout will not work on Internet Explorer

On my website I have CD offers that rotate through every five seconds. This works on all browsers apart from internet explorer where it remains on the first CD.
function updateTarget( txt ) {
'use strict';
document.getElementById('offers').innerHTML = txt;
}
window.onload = function() { // attach an onload handler
getOffers(); // call the function below, getQuotes()
};
var getOffers = function() {
// call getRequest with the url, the callback, displayQuotes, and false meaning not xml
getRequest('getOffers.php', updateTarget, false);
setTimeout(getOffers, 5000); // set a timer to call this same function again in 5 seconds
};
I have tried setting the date in the header to a date in the past but this did not work. I have tried to use setinterval but again no luck.
Any help on this would be appreciated.

setTimeout continuous loop without wait

I'm trying to get setTimeout to re-run the function it's inside after 15 seconds, it's not waiting 15 seconds and just doing it in a constant loop.
Here's my current code
function checkSession(x) {
http.abort();
http.open("GET", siteURL+"processes/ajax.php?call=check_session&string="+x+"&new="+Math.random(), true);
http.onreadystatechange = function() {
if(http.readyState == 4) {
if(http.responseText == true) {
updateSession(x);
} else {
setTimeout(checkSession(x),15000);
}
}
}
http.send(null);
}
I don't see any problems in the code itself, the only thing wrong is that it's just doing a constant loop without waiting the "15000" miliseconds.
change the setTimeout call to:
setTimeout(function(){checkSession(x)},15000);
As you have it now, checkSession is called immediately and then passed as an argument to setTimeout. Wrapping it inside the function allows for the call to be deferred.
Your explanation:
The function is like this: setTimeout( function, delay );
Your method call was not setting an anonymous function or reference to a function as the function argument.
Wrong: setTimeout(checkSession(x),15000);
Reason: checkSession(x) is a function call, not a reference to a function or anonymous function
Right: setTimeout(function() {checkSession(x) },15000);
Reason: the function call is now wrapped as an anonymous function in place and the function argument is set for the setTimeout( function, delay ) method.
Hope that helps to clear it up for you!

Implementing timeouts for node.js callbacks

This is a typical situation in node.js:
asyncFunction(arguments, callback);
When asynFunction completes, callback gets called. A problem I see with this pattern is that, if asyncFunction never completes (and asynFunction doesn't have a built-in time-out system) then callback will never be called. Worse, it seems that callback has no way of determining that asynFunction will never return.
I want to implement a "timeout" whereby if callback has not been called by asyncFunction within 1 second, then callback automatically gets called with the assumption that asynFunction has errored out. What is the standard way of doing this?
I'm not familiar with any libraries that do this, but it's not hard to wire up yourself.
// Setup the timeout handler
var timeoutProtect = setTimeout(function() {
// Clear the local timer variable, indicating the timeout has been triggered.
timeoutProtect = null;
// Execute the callback with an error argument.
callback({error:'async timed out'});
}, 5000);
// Call the async function
asyncFunction(arguments, function() {
// Proceed only if the timeout handler has not yet fired.
if (timeoutProtect) {
// Clear the scheduled timeout handler
clearTimeout(timeoutProtect);
// Run the real callback.
callback();
}
});
You probably need to come out with a solution of your own. Like
function callBackWithATimeout (callback, timeout) {
var run, timer;
run = function () {
if (timer) {
clearTimeout(timer);
timer = null;
callback.apply(this, arguments);
}
};
timer = setTimeout(run, timeout, "timeout");
return run;
}
and then
asyncFunction(arguments, callBackWithATimeout(callback, 2000));
You could do something like this:
function ensureExecution(func, timeout) {
var timer, run, called = false;
run = function() {
if(!called) {
clearTimeout(timer);
called = true;
func.apply(this, arguments);
}
};
timer = setTimeout(run, timeout);
return run;
}
Usage:
asyncFunction(arguments, ensureExecution(callback, 1000));
DEMO
But note the following:
The timeout is started immediately when you call ensureExecution, so you cannot cache that function reference.
The arguments passed to the callback will differ. For example asyncFunction might pass some arguments to callback upon success, but if the function is called by the timeout, no arguments will be passed. You have to keep that it mind. You could also provide default arguments with which the function should be called in this case:
function ensureExecution(func, timeout, args, this_obj) {
// ...
timer = setTimeout(function() {
run.apply(this_obj, args);
}, timeout);
//...
}
I ran into the same problem with a content script trying to open the port on the BG extension before the BG extension was ready. A work around was to wait for the BG extension to reply to a message and repeat this till successful. Here are the code snippets.
Content Script:
var nTimes = 10;
var bIsReady = false;
checkBGReady();
function checkBGReady() {
if (!bIsReady) {
chrome.runtime.sendMessage({msgText: "hello "+nTimes}, function(response) {
if (response && response.ack) {
console.log("have response:"+response.ack+" "+nTimes);
bIsReady = true;
// continue with initialization
bootStrap(sURL);
checkReady();
} else {
console.log("have no ack response %o",response);
}
});
}
nTimes -= 1;
if (nTimes > 0 && !bIsReady) {
setTimeout(checkBGReady,100);
}
}
BG Extension
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(sender.tab ?"from a content script:" + sender.tab.url :"from the extension");
if (request.msgText) {
console.log("Have msg "+request.msgText);
sendResponse({ack: "have contact "+request.msgText});
}
});
In my case it usually took after the first 100ms delay.

Categories

Resources