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);
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 am very new to JS, trying to create simple page which does next:
takes IP of some server
then sends a get request to this server
parses get response,
adds filtered lines to the table on html page.
I was able to do all the steps through the browser console but when, moving to the JS file with get function for some reason function does not return value.
In below code snip line 6 will print undefined in the console.
Any idea how to return "statuses" from the function getStatus?
Should it be some timeout between line 5 and 6?
Thanks!
$("input[type='text']").keypress(function(event){
if(event.which === 13){
var address = $(this).val();
var urlStat = 'http://'+address+':666/bla?open=stats';
var status = getStatus(urlStat);
console.log(status);
$("input[type='text']").val('');
$('table').append("<tr><th>"+address+"</th><th><ul></ul></th><th></th></tr>");
}
});
function getStatus(url){
var xhr = new XMLHttpRequest;
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var regexStatus = /(\w+ state:.*?)</g
var response = xhr.responseText;
var statuses = response.match(regexStatus);
console.log('Inside function getStatus'+statuses);
return statuses;
};
}
};
The problem with your code is that the status is returned after your your request has been sent. That gives a small delay. Because you immediatly ask for the return value of getStatus, you will get undefined.
You could solve this problem with a callback function:
function getStatus(url,callback){
var xhr = new XMLHttpRequest;
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var regexStatus = /(\w+ state:.*?)</g
var response = xhr.responseText;
var statuses = response.match(regexStatus);
console.log('Inside function getStatus'+statuses);
if(callback) callback(statuses);
};
}
};
You call the getStatus function with a function, which is called after you got a response from you request.
E.g:
getStatus(url,function(statuses){
console.log(statuses);
});
EDIT
For a better and longer explanation, consider to check out How do I return the response from an asynchronous call?
I have an Image that needs to be displayed based on the outcome of the AJAX Call. I don't load the jQuery and other Libraries like Foundation which is responsible for loading responsible image based on the device.
The response time of the AJAX call ranges from 800-1000 ms. If the response takes more than 1000 ms I would display a default image. Also I should send the AJAX request as the first request on load , hence it is set immediately as the first child of header.
JSFiddle for Timing issue
<html><head><script>set Timeout of 1000 ms .....xhr.send();</script>
<body>
<div id="id_in_response" style="display:none"><img src="xyz"></div>
<div id="default_on_timeout" style="display:none"><img src="xyz"></div>
....Loads of Elements.....
<footer>
<script src="jquery.js"></script>
<script src="foundation.js"></script>
<script src="custom.js"></script>
</body>
Explanation of custom js :
Custom js will execute a Foundation library js to load the responsive image.
The problem is how should the XHR communicate with custom.js function that there is either a TIMEOUT OR Response has to be processed. I cannot use jQuery Promise because the jQuery will load after the HTML is downloaded. I cannot use Native Promises yet .
It can be that while the XHR response comes in , but the custom.js has still not loaded or getting parsed. I also cannot assume that resonse time will be always be in the range of 800-1000 ms. It can even come down to 300 ms or less.
Custome JS code :
$(document).foundation({interchange : named_queries {....}});
// This will parse all the image tags , Run media Query and attach an
appropriate source to the image
Final Solution : enter link description here
poor mans "future" code
var specialRequest = (function() {
var cb;
var cberr;
var xhr = new XMLHttpRequest();
var doCallback = function(err) {
if (cb === undefined) {
cb = true;
cberr = err;
} else if (typeof cb === 'function') {
cb(err, xhr);
}
};
var timeout = setTimeout(function() {
xhr.abort(); //??
doCallback('timeout');
}, 1000);
xhr.open('GET', 'whatever/your/url/is');
xhr.onload = function() {
doCallback(null);
}
xhr.onerror = function() {
doCallback('error');
}
xhr.onloadend = function() {
// if the XHR finishes before the setTimeout, cancel it
clearTimeout(timeout);
}
xhr.send();
return function(callback) {
if (cb === undefined) {
cb = callback;
} else if (cberr !== null) {
callback(cberr, xhr);
}
}
})();
and then, in custom.js
specialRequest(function(error, xhr) {
if (error) {
// handle error
} else {
// handle success
}
});
compare this with Promise code
var myPromise = new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
var timeout = setTimeout(function() {
reject(new Error('timeout'));
}, 1000);
xhr.open('GET', 'whatever/your/url/is');
xhr.onload = function() {
resolve(xhr);
}
xhr.onerror = function() {
reject(new Error('error'));
}
xhr.send();
});
// in custom.js
myPromise.then(function(xhr) {
// handle success
}).catch(function(reason) {
// handle failure
});
should i use setInterval - wouldnt that block the page ?
No, it wouldn't; but you'd use setTimeout, not setInterval, to establish your 1000ms timeout. E.g., roughly:
var timer = setTimeout(function() {
// Use default image instead
}, 1000);
var xhr = new XMLHttpRequest(/*...*/);
xhr.send(/*...*/);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
if (/*...you haven't already used the default image...*/) {
clearTimeout(timer);
// Use the appropriate image...
}
}
};
Note that the above doesn't hold up the display of the page, which I strongly recommend not doing. Just fill in the image when you know what image to fill in (as above).
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'
});
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;