jQuery Long Polling (With PHP server side)
ajax, getjson, jquery, json, php
Solution
You must be sure that the function terminates inside a reasonable time. What you could do is this:
$ttl = 10;
while ($ttl--) {
$json = Yii::app()->user->getNotifications();
if (null != $json) {
break;
}
sleep(1);
}
if (null == $json) {
$json = json_encode(array(
'nothing' => true
));
}
header('Content-Type: application/json');
echo $json;
Yii::app()->end();
return;
You set up the polling function as a timer using setInterval(). The function will now be called every, say, 10 seconds, and you may need to set up a semaphore to avoid it being called before the previous iteration has returned:
var timer = setInterval(
function() {
if (this.calling) {
return;
}
var fn = this;
fn.calling = true;
$.post(url)
.done(function(data) {
..
})
.always(function() {
fn.calling = false;
});
},
10000
);
Then the polling function in AJAX needs to check (in the `.done()`) callback) that the notification is there:
function(data) {
if (data.hasOwnProperty('nothing')) {
alert('No notifications');
return;
}
console.log(data);
...
}
Now one important thing is what does your notification look like. Here I've assumed it is a JSON encoded string. But if it is an array or object that the Yii function returns instead, you need to handle its encoding. This might be even cleaner, without any IF's:
header('Content-Type: ...
die(json_encode(
array(
'status' => 'success',
'notification' => $json /* This is NULL or an array */
)
// Javascript side we check that data.notification is not null.
));
The decoding is already handled by jQuery, so the variable "data" above will already be a Javascript object, and you need not call `JSON.parse`. You can check that `data` is an object though, and that it has the expected properties. That will warn you of any errors.
To handle the navigation to another page, you can store the `setInterval()`-supplied timer ID of the polling Javascript function in a global variable, and delete the timer when the page calls `onUnload()` to deactivate the polling.
Problem
I'm having a bit of a nightmare here, so any help would be gratefully appreciated! Firstly, I'll explain what I'm trying to do: I'm trying to implement a system like described here: https://stackoverflow.com/a/1086448/1034392 on my localhost MAMP server using Yii framework. I have a function that checks if there are any new notifications in the DB - if so, it parses them and json encodes them. I have this function called on a while loop every 5 secs. So: going to /user/unreadNotifications triggers the following ``` Yii::log('test'); // to check it's getting called $this->layout=false; header('Content-Type: application/json'); // LONG POLLING while (Yii::app()->user->getNotifications() == null) { sleep(5); } echo Yii::app()->user->getNotifications(); // prints out json if new notification Yii::app()->end(); return; ``` This works fine - gone to the link in browser and verified json response - all good. I have then tried all sorts of jQuery stuff to get it working... The ONLY way I have found to work is using `$.ajax` with type POST but ONLY when there is a waiting notification (so some json is returned). `$.get` or `$.post` gets "aborted" (displayed in firebug) but the URL is called (because I can see the log file is updated) - odd. My original setup using `$.get` is: ``` <script type="text/javascript"> function notificationPoll() { $.get('<?php echo Yii::app()->createUrl('user/unreadNotifications') ?>','', function(result) { $.each(result.events, function(events) { alert('New Notification!'); }); notificationPoll(); }, 'json'); } </script> <script type="text/javascript"> $(document).ready(function() { $.ajaxSetup({ timeout: 60 //set a global ajax timeout of a minute }); notificationPoll(); }); </script> ``` This just gets "aborted" for some reason. I've tried with 'jsonp' even though it is not a CORS request.. but that doesn't work either. Can't seem to get anywhere with this! Can anyone chip in? Many thanks