In my project, I have a long-running process which is called by AJAX. Duration can be 1 to 15 mins.
While AJAX is running, I want to give updates to users. It just should show simply how many rows left to add into the database.
I found out that there are a few different options to realize this. Polling, SSE or WebSockets. I never worked with WebSockets, and I couldn't find a good example.
I'm trying now with SSE which I quite understand, and it is working properly.. but when the AJAX start running the connection to the eventSource will be pending. So while AJAX is running, there are no updates received.
Code:
<script type="text/javascript">
var es;
function checkProgress(id){
es = new EventSource('checkProgress.php');
es.addEventListener('message', function(e) {
console.log(e.data);
}, false);
}
checkProgress(1);
$(function() {
$('.submit').on('click', function() {
var form = $('form')[0];
var form_data = new FormData(form);
$.ajax({
type: 'POST',
url: 'submit.php',
contentType: false,
processData: false,
data: form_data,
success:function(response) {
console.log(response);
}
});
return false;
});
});
</script>
Screenshots:
Network log
Now actually I still didn't find any reference or example of how to implement SSE while there is an AJAX process running. All reference or examples give examples to let the getProgress file to do something.
PHP:
checkProgress.php
<?php
require_once './core/init.php';
$Jobs = new Core\Jobs();
$data = $Jobs->getUpdate(1);
// var_dump($data);
// die();
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache');
echo "id: 1" . PHP_EOL;
echo "retry: 100\n\n";
echo "data: " . json_encode($data) . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
Submit.php
<?php
require_once 'core/init.php';
//'long running' job for debug
for($i = 1; $i <= 10; $i++) {
Core\Jobs::pushUpdate('1', array("rows"=>$i,"job_id"=>1));
sleep(2);
}
Related
I need some example to display POST data inside HTML DIV element. Like this: Beeceptor
I make an example using PHP and jQuery.
It works fine but I don't know if there a better solution instead of using SESSIONS and interval function?
The POST data is made by using an external program (not by jQuery itself).
PHP
session_id('13245');
session_start();
$session_id = session_id();
if($data['payload'] !== null)
{
$_SESSION['payload'] = $data['payload'];
$_SESSION['timestamp'] = microtime();
}
else
{
$_SESSION['payload'] = $_SESSION['payload'];
$_SESSION['timestamp'] = $_SESSION['timestamp'];
}
echo json_encode(array('timestamp' => $_SESSION['timestamp'], 'payload' => $_SESSION['payload']));
?>
jQuery
$(document).ready(function () {
var oldTimeStamp = 0;
setInterval(function()
{
$.ajax({
type:"post",
url:"post.php",
datatype:"json",
success:function(data)
{
var obj = jQuery.parseJSON(data)
if(oldTimeStamp != obj.timestamp)
{
oldTimeStamp = obj.timestamp;
$('#displayData').append('timestamp: ' + obj.timestamp);
$('#displayData').append(' rawPayload: ' + obj.payload);
$('#displayData').append('<br />');
}
}
});
}, 1000);//time in milliseconds
});
Any help will be greatly appreciated.
you can go for "then()" or "done()", immediate after finishing ajax call. here is the sample:
$.ajax({
type:"post",
url:"post.php",
datatype:"json",
success:function(data)
{...}
}).then(function (data){
var obj = jQuery.parseJSON(data)
if(oldTimeStamp != obj.timestamp)
{
oldTimeStamp = obj.timestamp;
$('#displayData').append('timestamp: ' + obj.timestamp);
$('#displayData').append(' rawPayload: ' + obj.payload);
$('#displayData').append('<br />');
}
});
You are trying to make a real-time application such as chatting and real-time visualizations. In order to achive this I suggest you to write with NodeJs SOCKET.IO
If you use PHP it will make your server lode more harder than JavaScript programs like socket.io.
Your Question:
It works fine but I don't know if there a better solution instead of using SESSIONS and interval function?
Answer:
Definitely it's a bad practice which trigger the server every seconds even there are no new updates. Let's assume you have 100 users online at the same time so your server will be called 100 times every second which is really a more load to the server.
Example:
https://socket.io/get-started/chat
I'm using JQuery and AJAX to call a slow function in a PHP file, but I don't get a response until the PHP function is complete. I can't figure out why, despite 2 days of searching. I've shrunk it down to two test files, that exhibit exactly the same behaviour. My php function is thus, in a file called "ajaxfuncs.php":
<?php
Class AjaxFuncs{
public function __construct() {
$this->testbuffer ();
}
function testbuffer(){
echo 'Starting Test Buffer Output. There should be a 2 second delay after this text displays.' . PHP_EOL;
echo " <br><br>";
echo '<div id="testdata" class="testdata">0</div>';
// The above should be returned immediately
flush();
// Delay before returning anything else
sleep(2);
for ($a = 0; $a < 3; $a++) {
echo '<script type="text/javascript">document.getElementById("testdata").innerHTML="' . (int)($a + 1) . '"</script>';
flush();
// Delay for 1 second before updating the value
sleep(1);
}
}
}
$AjaxFuncs = new AjaxFuncs();
?>
The above works if I open the "ajaxfuncs.php" file in the browser. It does exactly as I'd expect, and the output updates every second until complete. So I know I've buffering sorted on the server.
But when I call it using the following $.ajax it's not right. I've put everything except the php for the ajax function into another php file called "testindex.php" for convenience. This is it:
<?php
header( 'Content-type: text/html; charset=utf-8' );
header("Cache-Control: no-store, must-revalidate");
header ("Pragma: no-cache");
header("Expires: Mon, 24 Sep 2012 04:00:00 GMT");
?>
<body>
<a>Test Page. Wait for it...</a>
<div id="maincontent">
</div>
</body>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js"></script>
<script>
$(document).ready(function(){
console.log ('Document Ready');
function callajax(){
var ajaxfilepath = "ajaxfuncs.php";
return $.ajax({
url: ajaxfilepath,
data: {action: 'testbuffer'},
type: 'get',
cache: false,
dataType: "text",
beforeSend: console.log('Starting Ajax Operation')
})
.done(function(result){
processajaxcalloutput(result);
})
.always(function(){
console.log(' Ajax Internal Complete Detected');
})
.fail(function( jqXHR, textStatus ) {
console.log( "Ajax Request failed: " + textStatus );
});
}
function processajaxcalloutput(result){
var message = 'Processing Ajax Response' ;
console.log(message);
$("#maincontent").append(result);
}
callajax();
});
Everything works without error. Console is clear - no errors (I'm testing with Chrome & Firefox). But I can't seem to get a response until the entire PHP function is done. I've tried everything I can find, in particular hundreds of different things to force caching off, but to no avail. Any help is much appreciated. Thanks.
Update:
So based on the feedback so far, it's clear the call is asynchronous and the code is fine, but asynchronous does not mean I'll get a continuous stream of output data from the php function as it executes. Instead the entire response will be returned at the end of the execution. Rather than divert this question into one about streaming, I'll leave it at this until I resolve the streaming issue.
I have two pages to test real-time communication: "client1" and "client2". Both pages running different ajax call (action) with same code every 500ms.
<script type="text/javascript">
var urlBase = "<?php echo Request::root(); ?>"
$(function(){
function requestData1() {
$.ajax({
url: urlBase + '/session/ajaxData1',
success: function(point) {
setTimeout(requestData1, 500);
},
cache: false
});
}
requestData1();
});
</script>
In my controller, I just pass data to redis.
public function ajaxData1()
{
header("Content-type: text/json");
$x = time() * 1000;
$y = rand(0, 100);
$ret = array($x, $y);
$redis = LRedis::connection();
$arrayJson = array();
array_push($arrayJson, array('"user"' => 'user2') + array('"data"' => $ret));
$redis->publish('message1', json_encode($arrayJson));
}
After some time Laravel automatically logout me. If I set setTimeout to 2000 everything works well. Why is it happening? Would it be the same when I get data from a real source (device)?
A solution that worked for me with this issue was to use database session storage.
http://laravel.com/docs/4.2/session#database-sessions
I haven't had a single forced-logout since, although I didn't spend any time afterwards further investigating the original issue. Life's just too short.
Some times it's occurred for csrf token. you have to pass the csrf token with post method.
Please see th example to solve the token related issues.
ajax: {
url: '{{url("registration/get-list")}}',
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}'
}
},
I want to create notification system in my company's erp similar to Facebook one. To maintain good performance, I use long polling - looped ajax querying php script for number of seconds.
Everything works fine, until I try to go to another page inside ERP. When I click any link on the page, everything freezes waiting until background php script is completed, even if I manually killed ajax connection.
JS script is included on every page and starts itself on page load.
function notificationsObject(){
var nl = new Object();
nl.startAjax = function(data){
if(nl.ajaxObject != null) try{ nl.ajaxObject.abort() } catch(e){} finally{nl.ajaxObject = null}
nl.ajaxObject = $.ajax({
url: nl.ajaxUrl, //declared before function declaration
type: 'POST',
data: {data: data}
}).done(function(responseText){nl.ajaxSuccess(responseText)
}).fail(function(responseText){nl.ajaxFail(responseText)});
}
nl.ajaxSuccess = function(response){
console.debug(response);
nl.startAjax();
}
nl.ajaxFail = function(response){
//#todo some code here
}
nl.killConnection = function(){
if(nl.ajaxObject != null) try{ nl.ajaxObject.abort() } catch(e){} finally{nl.ajaxObject = null}
console.debug('killing');
}
(more code here)
return nl;
}
init code looks like this
$(document).ready(function(){
var notifications = notificationsObject();
notifications.startAjax({name: 'startup'});
setTimeout(function(){window.onbeforeunload = function(){notifications.killConnection()};}, 1000);
});
and there's also some PHP code:
public function executeUsersNotificationListener(){
ignore_user_abort(false);
ob_end_flush();
$this->getResponse()->setHttpHeader("Cache-Control", "no-cache");
$this->getResponse()->setHttpHeader("Pragma", "no-cache");
$this->getResponse()->setHttpHeader("Expires", 0);
$timeLimit = 30;
set_time_limit($timeLimit+1);
echo 'test';
$i = 0;
while($i++ < $timeLimit){
echo " ";
sleep(1);
}
return sfView::NONE;
}
as you can see above, I did some research and used ignore_user_abort and so on, but it won't work.
I'm playing a bit around with push notifications, and want to update a page whenever there's a change in the database.
I have this from http://www.screenr.com/SNH:
<?php
$filename = dirname(__FILE__).'/data.php';
$lastmodif = isset($_GET['timestamp']) ? $_GET['timestamp'] : 0;
$currentmodif = filemtime($filename);
while ($currentmodif <= $lastmodif) {
usleep(10000);
clearstatcache();
$currentmodif = filemtime($filename);
}
$response = array();
$response['msg'] = file_get_contents($filename);
$response['timestamp'] = $currentmodif;
echo json_encode($response);
?>
My data.php is a script getting data from a JSON file:
<script>function itnews_overview() {
$.getJSON('/ajax.php?type=itnews_overview', function(data) {
$.each(data.data, function(option, type) {
$('.bjqs').append('<li><span class="date">'+ type.submitted +'<br />'+ type.time +'</span><h2>' + type.title + '</h2><p>' + type.content + '</p></li>');
});
});
}
</script>
<script>
itnews_overview();
</script>
<div id="news">
<ul class="bjqs"></ul>
</div>
UPDATE: Code from index.php:
<script type="text/javascript">
var timestamp = null;
function waitForMsg() {
$.ajax({
type: "GET",
url: "getData.php?timestamp=" + timestamp,
async: true,
cache: false,
success: function(data) {
var json = eval('(' + data + ')');
if(json['msg'] != "") {
$(".news").html(json['msg']);
}
timestamp = json['timestamp'];
setTimeout('waitForMsg()',1000);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
setTimeout('waitForMsg()',15000);
}
});
}
$(document).ready(function(){
waitForMsg();
});
</script>
As this file isn't saved when I add something to the database, filemtime won't work — is there another way I can check if new rows has been added to the table?
UPDATE: Trying to solve this with SSE.
I have two files, index.php and send_sse.php (inspiration from http://www.developerdrive.com/2012/03/pushing-updates-to-the-web-page-with-html5-server-sent-events/)
index.php:
<div id="serverData">Content</div>
<script type="text/javascript">
//check for browser support
if(typeof(EventSource)!=="undefined") {
//create an object, passing it the name and location of the server side script
var eSource = new EventSource("send_sse.php");
//detect message receipt
eSource.onmessage = function(event) {
//write the received data to the page
document.getElementById("serverData").innerHTML = event.data;
};
}
else {
document.getElementById("serverData").innerHTML="Whoops! Your browser doesn't receive server-sent events.";
}
</script>
send_sse.php:
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$url = "content.json";
$str = file_get_contents($url);
$data = json_decode($str, TRUE);
//generate random number for demonstration
//echo the new number
echo "data: " . json_encode($data);
ob_flush();
?>
This, however, doesn't seem to work, which is probably because SSE needs plain text data. I just can't figure out how to do that and then wrap that content in a couple of HTML tags.
UPDATE: Okay, so now it's sort of working with SSE, thanks to VDP. I have the following:
$sql= "SELECT title, content, submitted FROM `flex_itnews` where valid = 1 order by submitted desc";
$query= mysql_query($sql);
setlocale(LC_ALL, 'da_DK');
while($result = mysql_fetch_array($query)){
echo "data: <li><span class='date'>". strftime('%e. %B', strtotime($result['submitted'])) ."<br />kl. ". strftime('%H.%M', strtotime($result['submitted'])) ."</span><h2>" . $result['title']. "</h2><p>" . $result['content'] ."</p></li>\n";
}
However, when I add anything new, it just echoes data: data: data. If I refresh the page, it displays correctly.
UPDATE: Using livequery plugin:
<script>
var source = new EventSource('data2.php');
source.onmessage = function (event) {
$('.bjqs').html(event.data);
};
$('#news').livequery(function(){
$(this).bjqs({
'animation' : 'slide',
'showMarkers' : false,
'showControls' : false,
'rotationSpeed': 100,
'width' : 1800,
'height' : 160
});
});
</script>
UPDATE: Trying to use delegate()
<script>
$("body").delegate(".news", "click", function(){
$("#news").bjqs({
'animation' : 'slide',
'showMarkers' : false,
'showControls' : false,
'rotationSpeed': 100,
'width' : 1800,
'height' : 160
});
var source = new EventSource('data2.php');
source.onmessage = function (event) {
$('.bjqs').append(event.data);
};
});
</script>
Yes! There are multiple (better) ways:
websocket (the best solution but not supported on older or mobile browsers)
Server sent events (SSE) (sort of polling but optimized just for the task you ask for)
Long polling (like you are doing)
Flash sockets
other plugin based socket stuff
ajax polling
I've posted another answer with examples about it before
I listed several transport methods. websockets being the ideal (because it's the only 2 way communication between server and client), SSE being my second choice. You won't need the $.getJSON method. The overall idea will be the same.
On the server side (php in your case) you query your database for changes. You return the data as JSON (json_encode(data) can do that). On the client side you decode the JSON (JSON.parse(data) can do that). With the data you received you update your page.
Just the polling like you where doing causes more overhead because you are doing lots of request to the server.
SSE is more "I want to subscribe to a stream" and "I want to stop listening". => less overhead
Websockets is more: "I set up a connection. I talk server listens. Server talks client listens" A full duplex connection. => least overhead
SSE Code example
The page the client goes to (for example index.html or index.php)
It's just a normal html page containing this javascript:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<script>
//javascript:
var source = new EventSource('data.php');
source.onmessage = function (event) {
//here you do the stuff with the received messages.
//like log it to the console
console.log(event.data);
//or append it to div
$('#response').append(event.data);
};
</script>
</head>
<body>
<div id="response"></div>
</body>
</html>
The 'data.php' page:
<?php
/* set the header first, don't echo/print anything before this header is set! Else the default headers will be set first then this line tries to set the headers and results in an error because the header is already set. */
header("Content-Type: text/event-stream\n\n");
//query the database
$sql= "SELECT COUNT(*) FROM `messages`";
$query= mysql_query($sql);
$result = mysql_fetch_array($query);
$count = $result[0];
//return the data
echo "data: " . $count. "\n";
?>
So you only need those 2 pages.
UPDATE:
I had only seen your comments not the updates.. sorry ;)
if you use .delegate() you shouldn't use body but try a selector as high up the tree as possible (.bjqs in your case).
In you're case you don't even need live,delegate,on or all that! Just apply the bjqs again afther the content is updated.
var source = new EventSource('data2.php');
source.onmessage = function (event) {
$('.bjqs').html(event.data);
$("#news").bjqs({
'animation' : 'slide',
'showMarkers' : false,
'showControls' : false,
'rotationSpeed': 100,
'width' : 1800,
'height' : 160
});
};
This will give you issues too because you are constantly re-initializing bjqs and it isn't written to handle dynamically updating content. What you can do is send only data (with php) if there is new data. Check if the call returns empty, if not update:
var source = new EventSource('data2.php');
source.onmessage = function (event) {
if(event.data !=""){
$('.bjqs').html(event.data);
$("#news").bjqs({
'animation' : 'slide',
'showMarkers' : false,
'showControls' : false,
'rotationSpeed': 100,
'width' : 1800,
'height' : 160
});
}
};
You can count number of rows in a table, and than check if the number of rows is changed.
Without digging into your code too much im answering the question in the title:
add last modified column to your table, this has a built in mysql trigger that updates whenever the row is added or changes:
ALTER TABLE `yourTable`
ADD COLUMN `last_modified` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
ADD INDEX (`last_modified`);
and then query it like,
SELECT * FROM yourTable where last_modified > ?
("?" is the pdo placeholder you replace with last queried timestamp)