Can't get long polling with jQuery and PHP to work - javascript

I'm been trying for many hours to get this working, just a basic long polling to a php script on the web server. I am testing it by having a text file which I check the last modified time of and compare it to the time the javascript sends a request. If the date of the file is newer then I send back a message "update". When I load the page in my browser it sends the request and waits as expected which I can verify using Chrome dev tools but then when I edit the file (ie. change the modified date) the update.php script never echos the update message. Instead it just continues until it times out.
I think it might have something to do with caching because if I copy the URL that the JS requests into a separate tab it also doesn't respond but if I change the random parameter that jQuery attaches to prevent caching I get the desired "update" message back. Any ideas what is wrong?
Thanks
The javascript running on the main page:
<script>
window.onload = function longPoll(){
var d = new Date;
$.ajax({
url: "http://localhost/site/update.php?time=" + d.getTime(),
success: function(data){
console.log(data);
},
error: function (xhr, ajaxOptions, thrownError) {
//error message here
},
type: "GET",
cache: false,
complete: longPoll,
timeout: 600000
});
}
</script>
The update script:
// while the file is NOT newer
while(!(filemtime('./test.txt') > $client_time)) {
sleep(1);
}
echo 'update';
?>

Somthing to keep in mind:
1: never trust what you got from client side like time.
2: The time you send to the server with client side is not really what you except, so if the user time is set wrong like 11 jun and in server your current time is 10 jun, the
filemtime('./test.txt') > $client_time
always return false
3: use clearstatcache() when you check file status
4: sleep(1) is too high, specially when you want to deploy high usage website, change that to more than 10 ms
5: as vher2 said, use this code for complete:
complete: function(jqXHR, status) {
if (status === 'success'){
setTimeout(function(){ longPoll() }, 1000);
}
}
Use this code to check current time on server and time time that received from user:
var_dump(time() , $client_time);

Try this:
complete: function(jqXHR, status) {
if (status === 'success'){
setTimeout(function(){ longPoll() }, 1000);
}
}

Related

AJAX/PHP/MySQL - Detect database changes and alert user?

Jquery:
$.ajax({
type: "GET",
dataType: 'html',
url: "order",
timeout: 30000,
cache: true,
success: function(data, status, xhr) {
$('#changes').append(data);
alert("x amount of changes have been updated");
},
error: function(xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
PHP:
$stmt = $db->prepare("SELECT * FROM `orders` WHERE `date` > NOW()");
//Other MySQL stuff
Basically I have no idea how I would do this, but I want to alert a user everytime there is a new order, but how exactly would I go about on the PHP side of alerting the user that there has been a change?
My brain is just not working today, I need to detect how many orders there have been and then append just the new orders to a table (And not the old ones)
Can I parse json from my order.php page but not display it and only display the HTML?
I really hope someone can help me out!
Yes, you can send and receive JSON from PHP. The following is from a production system, but I have removed a lot of code from it just to show you the important stuff. You will need to add error checking logic, etc (the code may have syntax errors (typos), but is, as I said, functional):
<script>
function checkForNew() {
var POSTData='CMD=getNewInvoices&'; /* Your PHP script can handle different requests */
POSTData=POSTData + 'myData=' + pseudoSave;
$.ajax( {
type: 'POST',
url: './checkForNewInvoices.php',
data: POSTData,
async: false,
error: function(data) {
retData=data;
// Do error stuff
},
success: function(data) {
retData=data;
retJ=JSON.parse(retData);
// Check here if valid JSON
}
});
setTimeout(function() { checkForNew(); }, 5000);
}
$(document).ready(function() {
checkForNew();
});
</script>
<?php {checkForNewInvoices.php}
...
...
echo json_encode($myJsonData); /* If data is in an array */
?>
Here is what I would do (logic/thinking {warning may be flawed :-)} ):
When I save the order (update/insert) I would update a text file (or db field) with an integer or 'flag (true/false)'.
My web-site, when loaded, will start with the count and the status of the flag, check for orders that don't have the processed flag and keep a local 'tag' of the loaded status.
I will then check if the flag/int has changed (order without the status flag). If it has changed or has orders without the 'shown/processed' status then I know there are new orders/invoices and I will then send a server query (ajax) to retrieve the orders or count, etc.
Worries/conditions:
If the orders come from all over and the web sites are accessed by lots of people (many to many).
The orders come from lots of people but only one person is loading the monitor site (many to one).
Order site is one place and monitor site is one (one to one - easy).
Trust this helps.
Happy Easter!

JQuery ajax method returning error during POST on Chrome

I have a web application written in ASP.NET MVC 4. Client side richly uses JQuery to perform requests to save and retrieve data, during the user experience. In certain moments the user can change some data and click a button-like link to save it. For architecture/performance/requirements restrictions, this process is made in two steps:
A POST request is sent to the server to a given URL (which will fire a certain action of a certain controller), containing a JSON object;
On the first POST success a timer is set to run a second POST to the same server, but different URL (another action in the same controller), with no content at all.
The second POST will just start a complementary process and conclude what was started by the first one. However, it never gets the server. The $.ajax method fires the error handler.
A simplified version of the first request code is
$.ajax({
url: self.opcoes.urlCreate,
type: "POST",
data: JSON.stringify(lancamento),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (data) {
self.LancarDia(250);
},
error: function (jqXHR, textStatus, errorThrown) {
alert("Não foi possível incluir o lançamento. O servidor retornou\n" +
ajaxErrorMessage(jqXHR, textStatus, errorThrown));
}
});
The method LancarDia() receives the miliseconds amount to set the timer for the second request, so the idea is to wait 250 miliseconds and then send the second request. The LancarDia() code is:
MyClass.prototype.LancarDia = function (milisegundos) {
var self = this;
if (milisegundos) {
if (self.timerLancarDia)
clearTimeout(self.timerLancarDia);
self.timerLancarDia = setTimeout(function () {
self.LancarDia();
self.timerLancarDia = undefined;
}, milisegundos);
return;
}
$.ajax({
url: self.opcoes.urlLancarDia + self._dataAtual.format("MM/dd/yyyy"),
type: "POST",
success: function (data) {
if (self.opcoes.onLancou)
self.opcoes.onLancou(self._dataAtual);
},
error: function (jqXHR, textStatus, errorThrown) {
var mensagem = "Não foi possível atualizar o MUMPS. Motivo:\n" +
ajaxErrorMessage(jqXHR, textStatus, errorThrown);
alert(mensagem);
}
});
}
Notice that the first POST always work and the second fails many times but not always. When it fails what I get is:
jqXHR.status == 0
jqXHR.readystate == 0
textStatus == "error"
errorThrown == ""
In its first version, the code didn´t use any timers. On the success of the first POST the second was immediatly sent. Changing to the current implementation was reported to reduce the frequency of the problem, but it still happens.
Only Chrome shows this problem, FireFox and IE run clean.
Have anyone faced and solved this problem?
Thanks in advance
You mention the second POST never gets sent to the server, how are you verifying this? It sounds more like a race condition where the second request is being sent before the server is ready for it (e.g. it is still doing something that was started by the first request).
After a long study, our infrastructure staff figured out that the problem was caused by the caching policy of the browser (Chrome) and the caching policy configured in the web server (IIS 7).
After configuring IIS to add no-cache in the cache-control response header, the problem disapeared.

Check Availability of a page before loading it, using jquery/ajax

Is it possible to check the Availability of a page before loading it?
I have a form, running on mobile device using wireless connection. The problem is: not always this connection is available and I would like to alert the user when is doing a submit or an unload of the page.
The problem is that the page contains elements doing redirect like this:
<input type="button" value="MyText" onClick="script1;script2;...window.location='mylocation'" />
If the user click on this button and the server is not achievable, i will receive some undesirable errors.
Also if I want to generalize my script i do not know the value of "mylocation" previously.
The page contains elements to submit the Form also:
<input type="submit" name="SUBMIT" value="MyValue" onClick="return eval('validationForm()')" />
For the submitting I'm using the ajaxForm plugin and it works quite well.
to navigate back easily use this instead:
<input type="button" value="Back" onClick="window.location='history.go(-1);" >
where -1 means previous page. If you want to reload the current page use 0 instead, to navigate forward, use 1, etc.
If you use ajax from jquery, it sould handle it by itself... http://api.jquery.com/jQuery.ajax/
$.ajax({
///... need argument here...
timeout: 5000, // in milliseconds
success: function(data) {
//Do something success
},
error: function(request, status, err) {
if(status == "timeout") {
alert("can't reach the server");
}
}
});
EDIT AFTER COMMENTS:
You can check How do I check if file exists in jQuery or JavaScript?
in your case this sould work as expected:
//Initialize the var as global so you can use it in the function below
var goto_url = "http://www.mywebsites.com/foo.html";
$.ajax({
url:goto_url;
type:'HEAD',
error: function()
{
//do something if the gile is not found
},
success: function()
{
document.location = goto_url; //docuemnt.location will redirect the user to the specified value.
}
});
this will actually check if the file exist.. If it can't connect to the file it will not be able to find it..
If he can find the file, he obviouly was able to connect, so either case you win.
cheers!
Thanks to your answer I found the solution to the problem.
This check if the server is achievable before launching a script and redirect.
That's the code:
function checkConnection(u,s){
$.ajax({
url:u,
cache:false,
timeout:3000,
error: function(jqXHR, textStatus)
{
alert("Request failed: " + textStatus );
},
success: function()
{
eval(s);
}
});
}
$(document).ready(function() {
// part of the function that checks buttons with redirect
// for any button that contain a redirect on onClick attribute ("window.locarion=")
$("input[type=button]").each(function(){
var script = $(this).attr("onClick");
var url = "my_url";
var position = script.indexOf("window.location") ;
if (position >= 0) { // case of redirect
url = "\'"+url+"\'"; // that's my url
script = "\""+script+"\""; // that's the complete script
$(this).attr("onClick","checkConnection("+url+","+script+")");
}
});
// part of the function that checks the submit buttons (using ajaxForm plugin)
var options = {
error: function() {
alert("Message Error");
},
target: window.document,
replaceTarget: false,
timeout: 3000
};
$("form").ajaxForm(options);
});
I hope that this will be usefull.
You should use the callback of the jQuery Ajax function to catch the problem of a server not available.
You cant check the servers' availibility without making a request to it.
You're trying to see if there is a connection. AFAIK the only way for actually checking if a server is reachable is making a request to that server.
Set a timeout of a reasonably small amount of time (let's say 3s) and make a request. If you get a timeout error, then there is no connection, else you're good to send the form.

Chrome not handling jquery ajax query

I have the following query in jquery. It is reading the "publish" address of an Nginx subscribe/publish pair set up using Nginx's long polling module.
function requestNextBroadcast() {
// never stops - every reply triggers next.
// and silent errors restart via long timeout.
getxhr = $.ajax({
url: "/activity",
// dataType: 'json',
data: "id="+channel,
timeout: 46000, // must be longer than max heartbeat to only trigger after silent error.
error: function(jqXHR, textStatus, errorThrown) {
alert("Background failed "+textStatus); // should never happen
getxhr.abort();
requestNextBroadcast(); // try again
},
success: function(reply, textStatus, jqXHR) {
handleRequest(reply); // this is the normal result.
requestNextBroadcast();
}
});
}
The code is part of a chat room. Every message sent is replied to with a null rply (with 200/OK) reply, but the data is published. This is the code to read the subscribe address as the data comes back.
Using a timeout all people in the chatroom are sending a simple message every 30 to 40 seconds, even if they don't type anything, so there is pleanty of data for this code to read - at least 2 and possibly more messages per 40 seconds.
The code is 100% rock solid in EI and Firefox. But one read in about 5 fails in Chrome.
When Chrome fails it is with the 46 seconds timeout.
The log shows one /activity network request outstanding at any one time.
I've been crawling over this code for 3 days now, trying various idea. And every time IE and Firefox work fine and Chrome fails.
One suggestion I have seen is to make the call syncronous - but that is clearly impossible because it would lock up te user interface for too long.
Edit - I have a partial solution: The code is now this
function requestNextBroadcast() {
// never stops - every reply triggers next.
// and silent errors restart via long timeout.
getxhr = jQuery.ajax({
url: "/activity",
// dataType: 'json',
data: "id="+channel,
timeout: <?php echo $delay; ?>,
error: function(jqXHR, textStatus, errorThrown) {
window.status="GET error "+textStatus;
setTimeout(requestNextBroadcast,20); // try again
},
success: function(reply, textStatus, jqXHR) {
handleRequest(reply); // this is the normal result.
setTimeout(requestNextBroadcast,20);
}
});
}
Result is sometimes the reply is delayed until the $delay (15000) happens, Then the queued messages arrive too quicly to follow. I have been unable to make it drop messages (only tested with netwrok optomisation off) with this new arrangement.
I very much doubt that delays are dur to networking problems - all machines are VMs within my one real machine, and there are no other users of my local LAN.
Edit 2 (Friday 2:30 BST) - Changed the code to use promises - and the POST of actions started to show the same symptoms, but the receive side started to work fine! (????!!!???).
This is the POST routine - it is handling a sequence of requests, to ensure only one at a time is outstanding.
function issuePostNow() {
// reset heartbeat to dropout to send setTyping(false) in 30 to 40 seconds.
clearTimeout(dropoutat);
dropoutat = setTimeout(function() {sendTyping(false);},
30000 + 10000*Math.random());
// and do send
var url = "handlechat.php?";
if (postQueue.length > 0) {
postData = postQueue[0];
var postxhr = jQuery.ajax({
type: 'POST',
url: url,
data: postData,
timeout: 5000
})
postxhr.done(function(txt){
postQueue.shift(); // remove this task
if ((txt != null) && (txt.length > 0)) {
alert("Error: unexpected post reply of: "+txt)
}
issuePostNow();
});
postxhr.fail(function(){
alert(window.status="POST error "+postxhr.statusText);
issuePostNow();
});
}
}
About one action in 8 the call to handlechat.php will timeout and the alert appears. Once the alert has been OKed, all queued up messages arrive.
And I also noticed that the handlechat call was stalled before it wrote the message that others would see. I'm wondering if it could be some strange handling of session data by php. I know it carefully queues up calls so that session data is not corrupted, so I have been careful to use different browsers or different machines. There are only 2 php worker threads however php is NOT used in the handling of /activity or in the serving of static content.
I have also thought it might be a shortage of nginx workers or php processors, so I have raised those. It is now more difficult to get things to fail - but still possible. My guess is the /activity call now fails one in 30 times, and does not drop messages at all.
And thanks guys for your input.
Summary of findings.
1) It is a bug in Chrome that has been in the code for a while.
2) With luck the bug can be made to appear as a POST that is not sent, and, when it times out it leaves Chrome in such a state that a repeat POST will succeed.
3) The variable used to store the return from $.ajax() can be local or global. The new (promises) and the old format calls both trigger the bug.
4) I have not found a work around or way to avoid the bug.
Ian
I had a very similar issue with Chrome. I am making an Ajax call in order to get the time from a server every second. Obviously the Ajax call must be asynchronous because it will freeze up the interface on a timeout if it's not. But once one of the Ajax calls is a failure, each subsequent one is as well. I first tried setting a timeout to be 100ms and that worked well in IE and FF, but not in Chrome. My best solution was setting the type to POST and that solved the bug with chrome for me:
setInterval(function(){
$.ajax({
url: 'getTime.php',
type: 'POST',
async: true,
timeout: 100,
success: function() { console.log("success"); },
error: function() { console.log("error"); }
});
}, 1000);
Update:
I believe the actual underlying problem here is Chrome's way of caching. It seems that when one request fails, that failure is cached, and therefore subsequent requests are never made because Chrome will get the cached failure before initiating subsequent requests. This can be seen if you go to Chrome's developer tools and go to the Network tab and examine each request being made. Before a failure, ajax requests to getTime.php are made every second, but after 1 failure, subsequent requests are never initiated. Therefore, the following solution worked for me:
setInterval(function(){
$.ajax({
url: 'getTime.php',
cache: false,
async: true,
timeout: 100,
success: function() { console.log("success"); },
error: function() { console.log("error"); }
});
}, 1000);
The change here, is I am disabling caching to this Ajax query, but in order to do so, the type option must be either GET or HEAD, that's why I removed 'type: 'POST'' (GET is default).
try moving your polling function into a webworker to prevent freezing up in chrome.
Otherwise you could try using athe ajax .done() of the jquery object. that one always works for me in chrome.
I feel like getxhr should be prefixed with "var". Don't you want a completely separate & new request each time rather than overwriting the old one in the middle of success/failure handling? Could explain why the behavior "improves" when you add the setTimeout. I could also be missing something ;)
Comments won't format code, so reposting as a 2nd answer:
I think Michael Dibbets is on to something with $.ajax.done -- the Deferred pattern pushes processing to the next turn of the event loop, which I think is the behavior that's needed here. see: http://www.bitstorm.org/weblog/2012-1/Deferred_and_promise_in_jQuery.html or http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/
I'd try something like:
function requestNextBroadcast() {
// never stops - every reply triggers next.
// and silent errors restart via long timeout.
getxhr = jQuery.ajax({
url: "/activity",
// dataType: 'json',
data: "id="+channel,
timeout: <?php echo $delay; ?>
});
getxhr.done(function(reply){
handleRequest(reply);
});
getxhr.fail(function(e){
window.status="GET error " + e;
});
getxhr.always(function(){
requestNextBroadcast();
});
Note: I'm having a hard time finding documentation on the callback arguments for Promise.done & Promise.fail :(
Perhaps it can be worked around by changing the push module settings (there are a few) - Could you please post these?
From the top of my head:
setting it to interval poll, would kinda uglily solve it
the concurrency settings might have some effect
message storage might be used to avoid missing data
I would also use something like Charles to see what exactly does happen on the network/application layers

How to detect Ajax call failure due to network disconnected

I am sending lots of data using jquery ajax method to web sever and client side respond only after receiving acknowledgment from server, now suppose network connection lost in MIDDLE of ajax call then how to detect this situation.
$.ajax({
url:'server.php',
data:'lots of data from 200KB to 5MB',
type:'post',
success: function(data)
{
alert('Success');
//some stuff on success
},
error: function(XMLHttpRequest, textStatus, errorThrown)
{
alert('Failure');
//some stuff on failure
}
});
This is my code and and it does not give error in middle of ajax call if get internet is disconnected.
NOTE : I cant use time out because data size is vary from 200kb to 5MB and server response time calculation is not feasible.
Try this:
First create a "ping" ajax call with setInterval every 5 seconds
function server_ping()
{
$.ajax({
url:"url to ping",
type: "POST"
});
}
var validateSession = setInterval(server_ping, 5000);
then arm your .ajaxError trap:
$(document).ajaxError(function( event, request, settings ) {
//When XHR Status code is 0 there is no connection with the server
if (request.status == 0){
alert("Communication with the server is lost!");
}
});
Remember Ajax calls are Asynchronous by default, so when the pings are going to the server and the request cannot reach the server the value on the XHR status is 0, and the .ajaxError will fire and you must catch the error and handle the way you want it.
Then you can send your data to the server, if the connection is lost when sending the data you get the error reported by the ping.
If your server was not very crowded, probably you could use a timer to start detecting the connection regularly when you start transferring the data (by using another ajax calling, for instance each 5 seconds). now you can use timeout.
Btw,
1)timeout doesn't always means network error. sometimes server's down also causes "timeout"
2)if the driver is down on client device, xhr.status = 0, and no timeout
I had a similar problem and solved it with a simpel try/catch and a re-try delay of (say) 2 seconds:
function myAjaxMethod()
{
try
{
$.ajax({ ... });
} catch (err)
{
console.log(err.message);
setTimeout(function(){myAjaxMethod(),2000});
}
}
I faced a similar situation like yours and fixed it by having a network check for every 5 seconds and if network is disconnected i would abort the ajax request manually which will end the ajax request.
Here i get the ajax XmlHttpRequest in the beforeSend event of the Jquery ajax call and use that object to abort the ajax request in case of network failure.
var interval = null;
var xhr = null;
$.ajax({
beforeSend: function(jqXHR, settings) {
xhr = jqXHR; // To get the ajax XmlHttpRequest
},
url:'server.php',
data:'lots of data from 200KB to 5MB',
type:'post',
success: function(data)
{
alert('Success');
//some stuff on success
},
error: function(XMLHttpRequest, textStatus, errorThrown)
{
alert('Failure');
//some stuff on failure
},
complete: function(data)
{
alert('Complete');
//To clear the interval on Complete
clearInterval(interval);
},
});
interval = setInterval(function() {
var isOnLine = navigator.onLine;
if (isOnLine) {
// online
} else {
xhr.abort();
}
}, 5000);
Try adding timeout: while constructing your $.ajax({}).
Also make sure to set cache: false, helpful sometimes.
Refer to Jquery's ajax() : http://api.jquery.com/jQuery.ajax/#toptions
You will get much more information there!
My thought s on your problem[updated]
#RiteshChandora , I understand your concern here. How ever I can suggest you to do 2 things.
As you have post data ranging from 200kb to 5mb, you might want to choose a maximum timeout. and trigger for the same. Yes, this might be problematic, but with the design you chosen, the only way to monitor the POST progress is to do this way. if not, see point 2.
I went through the flow, you are asking the user to copy the response Json from FB to your url. there are some problems here,
The json data has sensitive information about the user, and he is posting it on a url without SSL encryption.
Why should you prompt the user to post the acquired data on to your server? it should be easier if you user sever side scripts. Also you should never post huge data from the client to the server in occasions like these, where you could retrieve the same form the FBserver->your sevrer on the server side.
My suggested solution : after the user is authenticated , retrieve his friends list on the server side. do whatever you want on the server side, and display the result on the users screen.
This way all the burden will be taken by your server, also there is no need for the user to do any nasty json posting on your url.
Btw, your app idea is cool.
error: function(xhr, textStatus, thrownError)
{
alert(xhr.status);
alert(thrownError);
alert(textStatus);
}
Try them..
TextStatus (besides null) are "timeout", "error", "abort", and "parsererror".
When an HTTP error occurs, thrownError receives the textual portion of the HTTP status, such as "Not Found" or "Internal Server Error."
If Internet disconnects,the response wont be received and maximum it would be a timeout message..

Categories