I'm trying to get a webworker to poll a web server interface on the same machine every second or so. Most articles I have read say to avoid setInterval and use setTimeout instead but I have yet to find an example that uses AJAX instead of Jquery.
The code I have so far is below:
(function poll() {
setTimeout(function() {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.status === 200) {
responseObject = JSON.parse(xhr.responseText);
var newContent = '';
newContent += responseObject.cmd;
console.log(newContent);
}
}
xhr.open('GET', 'http://localhost:8194/screen_update/1000', true);
xhr.send(null);
setTimeout(poll, 1000);
}, 1000);
})();
The preferred output would be to poll the server each second which should in theory be more than adequate for the response to come through. I only want one request on the go at a time so if I end up with a request taking more than a second it just dumps the request (rather than queuing it) and issues a new request.
The above code polls okay but doesn't complete for 2 seconds so I've obviously got my setTimeout mixed up somewhere. Where do I correct this code?
I did just that a few days ago.. and while it may not be the most elegant, it works fine so far.
I have the worker handle the timeout / check interval, not the main JS. So I guess that's one more thing that the UI doesn't need to handle. Here is my worker code:
function checkStatus() {
console.log("statusCheck started");
var ajaxRequest;
try { ajaxRequest = new XMLHttpRequest(); // Opera 8.0+, Firefox, Safari
} catch (e) { try { // Internet Explorer Browsers
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) { try {
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) { // Something went wrong
console.error("AJAX not possible");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function() {
if(ajaxRequest.readyState == 4) {
self.postMessage(ajaxRequest.responseText);
var timer;
timer = self.setTimeout(function(){
checkStatus();
}, 1000);
}
}
ajaxRequest.open("GET", "/worker_statusCheck.php", true);
ajaxRequest.send(null);
}
this.onmessage = function(e){
checkStatus(); // the message comes in just once on pageLoad
};
Define a variable that determines if ajax finished or not. If function is called while ajax hasn't finished yet, you can exit the function and wait for the next call.
var stillWorking = false;
(function poll() {
if(stillWorking) return false;
stillWorking = true;
setTimeout(function() {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.readyState == 4) stillWorking = false;
if (xhr.status === 200) {
responseObject = JSON.parse(xhr.responseText);
var newContent = '';
newContent += responseObject.cmd;
console.log(newContent);
}
}
xhr.open('GET', 'http://localhost:8194/screen_update/1000', true);
xhr.send(null);
setTimeout(poll, 1000);
}, 1000);
})();
You can call same function when you get response of AJAX. In this way no need to check that currently AJAX is in process or not.
function poll() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange= function() {
if (xhr.status === 200) {
responseObject = JSON.parse(xhr.responseText);
var newContent = '';
newContent += responseObject.cmd;
console.log(newContent);
}
if (xhr.readyState == 4)
{
setTimeout(function(){ poll();},1000);
}
}
xhr.open('GET', 'http://localhost:8194/screen_update/1000', true);
xhr.send(null);
};
setTimeout(function(){ poll();},1000);
If you want to use onload callback then callback code should be
xhr.onload= function() {
if (xhr.status === 200) {
responseObject = JSON.parse(xhr.responseText);
var newContent = '';
newContent += responseObject.cmd;
console.log(newContent);
}
setTimeout(function(){ poll();},1000);
}
Because you are using HTML5 WebWorker, probably, you can use window.fetch which uses promises (https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), I think that browser support is almost the same.
Here is an example:
((url, INTERVAL, configs) => {
const MAX_ERRORS = 4;
let errors = 0;
var poll = () => window.setTimeout(getData, INTERVAL);
function getData() {
return window
.fetch(url, configs)
.then(res => res.json())
.then(data => {
errors = 0;
poll();
return data;
})
.then((data) => {
console.log("new data available", data);
})
.catch(() => {
if(errors >= MAX_ERRORS) {
console.log("GIVING UP");
return;
}
errors += 1;
return poll();
})
;
}
return poll();
})("http://jsonplaceholder.typicode.com/posts/1", 1000, {
method: 'GET'
});
Related
I'm working on a firefox extension and using browser.webRequest.onBeforeRequest.addListener.
I need to redirect or release the webRequest until I have information back from calls made within the handler.
I used to use synchronous ajax, but the page was blocked for too long when the network was poor. If the request time exceeds 5 seconds, I want to cancel the ajax request. But it seems impossible to set a timeout on a synchronous call.
These days I try to use asynchronous ajax and use the methods(return new Promise) provided in https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/onBeforeRequest. I tried several days but failed.
My codes:
var LOOKUP_URL = "https://a.b.c.com/";
var urlLookup = new UrlLookup(LOOKUP_URL);
var blockList = [];
browser.webRequest.onBeforeRequest.addListener(redirectAsync, {urls: ['<all_urls>']},, ["blocking"]);
function redirectAsync(details) {
var url = details.url;
return new Promise(function(resolve,reject){
var urlLookupResult = urlLookup.check(url, LookupComplete);
if(urlLookupResult.result){
var redirectUrl = urlLookupResult.url;
resolve({redirectUrl})
}
})
}
function LookupComplete(url, data, error){
if(data.result){
blockList.push(url);
localStorage.setItem("blockList",JSON.stringify(blockList));
return {
result: true,
url: "https://aaa.bbb.com/alerts.php?url=" + url;
}
}else {
return null
}
}
codes in urlLookup.js:
function UrlLookup(domain) {
function check(url, callback) {
var data;
var http_request = new XMLHttpRequest();
var timeId = window.setTimeout(function(){
http_request.abort();
},5000)
http_request.open("GET", domain + 'url='+url);
try {
http_request.send();
http_request.onreadystatechange = function(){
if(http_request.readyState == 4 && http_request.status == 200){
window.clearTimeout(timeId);
data = JSON.parse(http_request.response);
return callback(url, data);
}
}
} catch (e){
return callback(url, null, "Error");
}
};
return {
check: check
};
}
How should I modify it?
I'm trying to return a json object following an XMLHttpRequest get request, and I come up short. I think that might be because it is asynchronous, but I really can't put my finger on how to make it work. What am I doing wrong?
$(document).ready(function() {
var apiEndpoint = 'http://someapiendpoint.com/'
//Helpers
function sendRequest(_path) {
var results = {}
req = new XMLHttpRequest()
req.open('GET', apiEndpoint+_path)
req.onreadystatechange = function() {
if (this.readyState === 4) {
results = JSON.parse(this.response)
}
}
req.send()
return results
}
// Action
console.log(sendRequest('client1/'))
}); // end document ready
You should use this construction
function sendRequest(_path, cb) {
req = new XMLHttpRequest()
req.open('GET', apiEndpoint+_path);
req.onreadystatechange = function() {
if (this.readyState === 4) {
cb(JSON.parse(this.response));
}
else{
cb(null);
}
}
req.send();
}
// Action
sendRequest('client1/', function(result){
console.log(result);
})
For asynchronous calls you need to use call backs
Since you are already using jQuery you can do the following:
$(document).ready(function() {
var apiEndpoint = 'http://someapiendpoint.com/';
function sendRequest(path, callback){
$.get(apiEndpoint+path, function(response){
callback(JSON.parse(response));
}, json).fail(function(){
console.log('Failed');
});
}
sendRequest('client1/', function(json){
if(json){
console.log(json);
}
});
});
I've got some code that checks if a user is connected to the internet. It works perfectly, but in the application I have things need to move quickly. In the case that they're connected to a really slow or non-functioning network my statement takes a long time to evaluate to false and really slows things down. If they're connected it retrieves the 1px image in no time at all.
My question is. How can I modify this function to return "false" if it takes more than a second to run the XML request? Can't seem to find an easy way to do this, but I am new to javascript so sorry if I'm missing something obvious.
function doesConnectionExist() {
var xhr = new XMLHttpRequest();
var file = "/assets/LuPixel.png";
var randomNum = Math.round(Math.random() * 10000);
xhr.open('HEAD', file + "?rand=" + randomNum, false);
try {
xhr.send();
if (xhr.status >= 200 && xhr.status < 304) {
return true; //alert("TRUE");
} else {
return false; //alert("FALSE");
}
} catch (e) {
return false; //alert("FALSE");
}
}
UPDATE: thought I needed a little more context.
What I'm trying to do with the above function is test whether a connection exists, and then access a certain URL if it does, if it doesn't, store some info in a local array. As soon as I add timeout however, it breaks the existing functionality.
ANother part that may be complicating things is that I'm actually loading up all survey questions inintially, then using javascript to cycle out which ones display. At the end of the survey it either submits results and refreshes the page (if connected to the internet) or just shows the first question again and continues storing results in the "responses" object (if not connected to the internet) additionally each time a question is clicked it also attempts to use the above doesConnectionExists() function to determine whether to submit results as the javascript transitions to the next question. the questions variable below contains the number of questions left to show
Here is all the javascript from the page.
<script>
//prevents scrolling
document.body.addEventListener('touchmove', function(event) {
event.preventDefault();
}, false);
var questions = {{count($questions)}};
var isIOS = ((/iphone|ipad/gi).test(navigator.appVersion));
var myDown = isIOS ? "touchstart" : "mousedown";
var myUp = isIOS ? "touchend" : "mouseup"
var responses = '';
// Hide every div except the first one
$('.survey-container:not(:first)').hide();
$('button').bind(myUp, finish);
//$('button').bind('touchcancel', cancel);
function finish(){
var answerId = this.id;
var survey_id = {{$survey->id}};
var location_id = {{$location_id}};
//CYCLE THROUGH QUESTIONS (via survey-container)
// Hide the current div when we click the link
$(this).parent().parent().next().show();
$(this).parent().parent().hide();
responses += answerId+'.'+Math.floor(Date.now() / 1000)+' ';
//add responses
if(doesConnectionExist() == true) { //old navigator.onLine == true
//send response and reset responses
new Image().src = '/updateresponses?location_id='+location+'&survey_id='+survey_id+'&responses='+responses;
responses = '';
}
//submit and reset responses here
//reset responses if submitted with: responses = '';
questions = questions-1;
if(questions == 0) {
$('.survey-container:first').show();
questions = {{count($questions)}};
if(doesConnectionExist() == true) {
$('.darken, .thankyou').show();
//submit results after a short timeout for thankyou message
setTimeout(function() {
new Image().src = '/updateresponses?location_id='+location+'&survey_id='+survey_id+'&responses='+responses;
location.reload();
//window.location.assign('/updateresponses?location_id='+location+'&survey_id='+survey_id+'&responses='+responses);
}, 800);
} else {
$('.darken, .thankyou').show();
setTimeout(function() {
$('.darken, .thankyou').fadeOut(400);
}, 1200);
}
} else {
$('.darken, .thankyou').show();
setTimeout(function() {
$('.darken, .thankyou').fadeOut(400);
}, 1200);
}
}
function doesConnectionExist() {
var xhr = new XMLHttpRequest();
var file = "/assets/lupoll_light.png";
var randomNum = Math.round(Math.random() * 10000);
xhr.open('HEAD', file + "?rand=" + randomNum, false);
try {
xhr.send();
if (xhr.status >= 200 && xhr.status < 304) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
}
</script>
You can use async requests with timeout and abort if time limit exceeded.
var xmlhttp = getXmlHttp()
xmlhttp.open("POST", "/someurl", true); // true - activate async request method
xmlhttp.onreadystatechange=function(){
if (xmlhttp.readyState != 4) return
clearTimeout(timeout) // clear timeout on readyState 4
if (xmlhttp.status == 200) {
// all ok
...
alert(xmlhttp.responseText);
...
} else {
handleError(xmlhttp.statusText) // error callback
}
}
xmlhttp.send("a=5&b=4");
// 10 second timeout
var timeout = setTimeout( function(){ xmlhttp.abort(); handleError("Time over") }, 10000);
function handleError(message) {
// error callback function
...
alert("error: "+message)
...
}
Add timeout to the xhr object:
xhr.timeout = 1000;
You can specify the timout for the request:
xhr.timeout = 1000;
Besides setting the timeout, you should make the function asynchronous. That way you can allow a bit longer timeout if you with, and the application will still not be sluggish.
function doesConnectionExist(callback) {
var xhr = new XMLHttpRequest();
var file = "/assets/LuPixel.png";
var randomNum = Math.floor(Math.random() * 10000);
xhr.timeout = 1000;
xhr.onreadystatechange = function() {
if (xhr.status >= 200 && xhr.status < 304) {
callback(true);
} else {
callback(false);
}
};
xhr.open('HEAD', file + "?rand=" + randomNum, true);
xhr.send();
}
I'm making an http request asynchronously using XMLHttpRequest:
xhr.open(method, uri, true);
When I send something:
xhr.send(something)
When the server is down, it throws the following error:
net::ERR_CONNECTION_REFUSED
How can I catch and handle this error? The standard try..catch block doesn't work as the request is asynchronous.
Thanks in advance.
Use the onerror event of the XMLHttpRequest:
function aGet(url, cb) {
var x = new XMLHttpRequest();
x.onload = function(e) {
cb(x.responseText)
};
x.onerror= function(e) {
alert("Error fetching " + url);
};
x.open("GET", url, true);
x.send();
}
var dmp = console.log.bind(console); // Dummy callback to dump to console
aGet("/", dmp) // Ok, uses onload to trigger callback
aGet("http://dgfgdf.com/sdfsdf", dmp); // Fails, uses onerror to trigger alert
I wrote a full solution to that problem. It works perfectly!
I have a function called networkOrfail which will try to resend the XMLHttpRequest each second, if the network is available. Otherwise, it'll ignore the request.
When the request is succeeded, that polling stops and the response is returned.
Here's how to detect whether the network is available:
function getNavigatorConection() {
return navigator.onLine;
}
Then, create your XMLHttpRequest:
function makeRequest() {
let xhr = new XMLHttpRequest();
xhr.open('GET', 'anypage/anotherpage', true);
xhr.timeout = 2000;
xhr.onload = function () {
// Your request is completed
if (xhr.readyState == 4 && xhr.status == 200) {
// You're in a successfully condition
}
};
xhr.ontimeout = function (e) {
// Your request timed out
};
xhr.send(null);
}
Now, define your polling method as follows:
function networkOrFail(callFunc, callTime) {
let connected = getNavigatorConection();
let callableTimes = callTime < 2000 ? 2000 : callTime;
let toursBegin = 3;
let tours = toursBegin;
let intervalId;
let request = function() {
intervalId = setInterval(function() {
let connected = getNavigatorConection();
if (tours > 0) {
if (connected) {
callFunc();
tours =0;
return false;
}
tours--;
alert("i tryied againt to resend for another time and it remain just "+tours+" to retry");
} else {
clearRequest();
tours =toursBegin;
}
}, callableTimes > 5000 ? 5000 : callableTimes);
};
let clearRequest = function() {
clearInterval(intervalId);
intervalId = null;
};
if (connected)
callFunc();
else
request();
}
Finally, call the send method through the polling method by passing it toghether with the timeout in minutes:
networkOrFail(makeRequest, 5000);
I have the below code:
sendRequest : function(data){
var me = this;
this._createData(data);
try{
this.req.open(this.method,this.page,true);
this.req.onreadystatechange=function()
{
if (this.readyState==4 && this.status==200)
{
if(this.responseText)
var response = eval('(' + this.responseText + ')');
else
response = null;
me.callBack(response);
return false;
}
}
this.req.send(this.data);
} catch(err){
me.callBack(response);
}
},
It works fine, and returns what I expect it to return, but when the connection is lost, it doesn't go into the catch block. What I want to know is how catch the request when server page is not available.
Here's an example from Microsoft's doc page for onreadystatechange:
function reportStatus()
{
if (oReq.readyState == 4 /* complete */) {
if (oReq.status == 200 || oReq.status == 304) {
alert('Transfer complete.');
}
else {
// error occurred
}
}
}
var oReq = new XMLHttpRequest();
oReq.open("GET", "http://localhost/test.xml", true);
oReq.onreadystatechange = reportStatus;
oReq.send();
Look where it says // error occurred.
There is a similar code example on this MDN documentation page.
I set a setTimeout before I send the Ajax call:
var timeout = window.setTimeout("functionToCallOnTimeout()", 2000);
inside functionToCallOnTimeout I stop the call:
oReq.current=null;
On a positive answer I clear the timeout:
timeout = null;