I have a page which uses jquery's ajax functions to send some messages.
There could be upwards of 50k messages to send.
This can take some time obviously.
What I am looking to do is show a progress bar with the messages being sent.
The backend is PHP.
How can I do this?
My solution:
Send through a unique identifier in the original ajax call.
This identifier is stored in a database(or a file named with the identifier etc), along with the completion percentage.
This is updated as the original script proceeds.
a function is setup called progress(ident)
The function makes an ajax call to a script that reads the percentage.
the progressbar is updated
If the returned percentage is not 100,
the function sets a timeout that calls itself after 1 second.
Check this if you use jQuery:
http://docs.jquery.com/UI/Progressbar
You can just supply the value of the bar on every AJAX success.
Otherwise, if you don't use JS Framework see this:
http://www.redips.net/javascript/ajax-progress-bar/
I don't have a way to test it, but it should go like this:
var current = 0;
var total = 0;
var total_emails = <?php $total_emails ;?>;
$.ajax({
...
success: function(data) {
current++; // Add one to the current number of processed emails
total = (current/total_emails)*100; // Get the percent of the processed emails
$("#progressbar").progressbar("value", total); // Add the new value to the progress bar
}
});
And make sure that you'll include jQuery along with jQueryUI, and then to add the #progressbar container somewhere on the page.
I may have some errors though ...
You will probably have to round the total, especially if you have a lot of emails.
You could have an animated gif load via .html() into the results area until your ajax function returns back the results. Just an idea.
Regarding the jquery ui progress bar, intermittently through your script you'll want to echo a numeric value representing the percent complete as an assigned javascript variable. For example...
// text example php script
if (isset($_GET['twentyfive-percent'])) {
sleep(2); // I used sleep() to simulate processing
echo '$("#progressbar").progressbar({ value: 25 });';
}
if (isset($_GET['fifty-percent'])) {
sleep(2);
echo '$("#progressbar").progressbar({ value: 50 });';
}
if (isset($_GET['seventyfive-percent'])) {
sleep(2);
echo '$("#progressbar").progressbar({ value: 75 });';
}
if (isset($_GET['onehundred-percent'])) {
sleep(2);
echo '$("#progressbar").progressbar({ value: 100 });';
}
And below is the function I used to get the progress bar to update its position. A little nuts, I know.
avail_elem = 0;
function progress_bar() {
progress_status = $('#progressbar').progressbar('value');
progress_status_avail = ['twentyfive-percent', 'fifty-percent', 'seventyfive-percent', 'onehundred-percent'];
if (progress_status != '100') {
$.ajax({
url: 'test.php?' + progress_status_avail[avail_elem],
success: function(msg) {
eval(msg);
avail_elem++;
progress_bar();
}
});
}
}
If I had to guess, I bet there is a better way... But this is the way it worked for me when I tested it.
Use this answered question
this is how i implemented it:
var progressTrigger;
var progressElem = $('span#progressCounter');
var resultsElem = $('span#results');
var recordCount = 0;
$.ajax({
type: "POST",
url: "Granules.asmx/Search",
data: "{wtk: '" + wkt + "', insideOnly: '" + properties.insideOnly + "', satellites: '" + satIds + "', startDate: '" + strDateFrom + "', endDate: '" + strDateTo + "'}",
contentType: "application/json; charset=utf-8",
dataType: "xml",
success: function (xml) {
Map.LoadKML(xml);
},
beforeSend: function (thisXHR) {
progressElem.html(" Waiting for response from server ...");
ResultsWindow.LoadingStart();
progressTrigger = setInterval(function () {
if (thisXHR.readyState > 2) {
var totalBytes = thisXHR.getResponseHeader('Content-length');
var dlBytes = thisXHR.responseText.length;
(totalBytes > 0) ? progressElem.html("Downloading: " + Math.round((dlBytes / totalBytes) * 100) + "%") : "Downloading: " + progressElem.html(Math.round(dlBytes / 1024) + "K");
}
}, 200);
},
complete: function () {
clearInterval(progressTrigger);
progressElem.html("");
resultsElem.html(recordCount);
ResultsWindow.LoadingEnd();
},
failure: function (msg) {
var message = new ControlPanel.Message("<p>There was an error on search.</p><p>" + msg + "</p>", ControlPanel.Message.Type.ERROR);
}
});
Related
I want to show a progress of data processed (in backend )on progress bar (on front).
For this 2 things am doing,
1] Sent one request to get session variable
interval = setInterval(function () {
$.getJSON('/importProgress', function (data) {
$('#putProgressPercent').html(data['progressPercent'] + "%");
$('#putProgressPercentMessage').html(data['progressMessage'] );
$("#progressBarLine").css("width", data['progressPercent'] + "%");
if (data['progressPercent'] == 100) {
clearInterval(interval);
$("#loaderModal").hide(); // hide div
}
});
}, 1000);
Initially will get 0, but when other (below) request is processing will get percentage
2] Send other request for data submission on backend side (in my case with for loop) like below
$.ajax({
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
url: "/storeData",
type: "post",
data: {
'someValue': someVaribaleToProcress,
},
success: function (response) {
var returnDetails = jQuery.parseJSON(response);
if (returnDetails['error']) {
alert(returnDetails['error']);
return false;
}
},
error: function (jqXHR, textStatus, errorThrown) {
clearInterval(interval);
}
});
and on back side this processes as below :
$chunksData = array_chunk($myData,3);
foreach ($chunksData as $index => $oneChunkData) {
foreach ($oneChunkData as $oneIndex => $details) {
$isImported = ImportData::addImportData($variables_1, $variables_2);
}
$parsedChunk += sizeof($oneChunkData);
$completedPercent = (round(($parsedChunk * 100) / sizeof($getTempTableData)));
echo $completedPercent. " : is percent <br>";
session()->put('importProgress', $completedPercent);
session()->save();
}
}
This $myData is huge. (suppose 1k now).
Now the problem is until this above for loop (of putting data in db) is completed, the interval hits are in pending state. so not getting updated with data.
even above line of printing percentage (echo $completedPercent. " : is percent";) is also not printing output until all data is processed.
can someone give me solution for this?
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 am trying to create a simple AJAX/PHP chat application to enable communication between registered users on a website. The problem with it is storing the chat history. I thought of storing all the chat messages in a single database (with columns like user1, user2, message, time), and then at every AJAX request, search the database for a matching message between the users, but I think this might be extremely inefficient. Is it a good idea to implement it this way, and what are some good ways of handling this?
Since a chat room has to constantly get updates, I wanted to minimize the requests to a minimum, so I just labeled each message with the database id. Since every user can see all the messages for that room, the request is just sending the id to the server to see if there were any new posts. If there were, it returns the new posts and updates the page.
Here's the JavaScript that I use:
var csrf = $("input[name='csrf_test_name']").val();
load_messages();
load_users();
$("#submitbutton").click(function(){
var message = $("#content").val();
$.post(base_url + "index.php/chat/ajax_post", {
text: message,
csrf_test_name: csrf
});
$("#content").attr("value", "");
return false;
});
function load_messages(){
var last = $('#messagewindow p:last').attr('id');
$.ajax({
url: base_url + "index.php/chat/ajax_retrieve",
type: "POST",
data: {
last: last,
csrf_test_name: csrf
},
cache: false,
success: function(html){
if(html.substr(1, 1) == 'p'){
var oldscrollHeight = $("#messagewindow").prop("scrollHeight") - 20;
var id;
var tag;
var user;
var uid;
//remove messages that exceed the max - determined on the server side
$('#messagewindow p:lt(' + $(html).siblings().size() + ')').remove();
//add messages, emoticons and classes based on whether its the user or someone else
$(html).find('b').each(function(){
if ($(this).html() == user_name + ':'){
$('#messagewindow').append($(this).parent().emoticons(base_url + 'images/emoticons').attr('class', 'self'));
} else {
$('#messagewindow').append($(this).parent().emoticons(base_url + 'images/emoticons').attr('class', 'others'));
}
});
//scroll screen
var newscrollHeight = $("#messagewindow").prop("scrollHeight") - 20;
if(newscrollHeight > oldscrollHeight){
$("#messagewindow").animate({ scrollTop: newscrollHeight }, 'normal');
}
$(html).find('span').each(function(){
id = $(this).attr('id');
uid = 'u' + id.substr(1);
if (id.substr(0, 1) == 'e' && $('#userwindow p[id="' + uid + '"]').size() < 1){
user = $(this).prev().html();
tag = "<p id='" + uid + "'>" + user.substr(0, user.length - 1) + "</p>";
$('#userwindow').append(tag);
} else if(id.substr(0, 1) == 'x') {
$('#userwindow p[id="u' + id.substr(1) + '"]').remove();
}
});
}
}
});
}
function load_users(){
$.ajax({
url: base_url + "index.php/chat/ajax_users",
cache: false,
success: function(html){
if(html.substr(1, 1) == 'p'){
$("#userwindow").html(html);
}
}
});
}
setInterval(load_messages, 2000);
setInterval(load_users, 240000);
What would be wrong with your method?
Use the proper MySQL indexes on your table (I would say on the two columns user1, user2 and on timestamp or unique ID for the order) and the performance will be good enough.
You can add the curent timestamp to the AJAX request to retreive only message that you didn't loaded the call before.
Turning off asynchronous requests in jQuery fixed the issue.
I have the following Javascript & AJAX request (using jQuery) in my page:
"use strict";
var hsArea, counter, hotspots, count;
counter = 4;
count = 0;
hotspots = {};
function fetchHotspotList() {
$.getJSON ('/alpha/engine/hotspots/gethotspot.php', {'type' : 'list'}, function(json) {
hotspots = json;
});
}
function displayHotspot(type, id, number) {
$.ajax({
url: '/alpha/engine/hotspots/gethotspot.php',
dataType: 'json',
data: {'type' : type, 'id' : id},
success: function(json) {
console.log(json);
var hotspot, extract;
extract = json.content;
extract = extract.replace(/<(?:.|\n)*?>/gm, '');
extract = extract.substring(0, 97);
extract = extract + "...";
json.content = extract;
hotspot = document.createElement("div");
hsArea.append(hotspot);
hotspot.setAttribute('class','hotspot');
hotspot.setAttribute('id','hotspot' + number);
$(hotspot).css('position', 'absolute');
$(hotspot).css('top', number * 100 + 100);
$(hotspot).css('left', number * 100 + 110);
hotspot.innerHTML = "<h1>"+ json.title + "</h1><p>" + json.content + "</p>";
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus, errorThrown);
}
});
}
function listHotspots() {
for(count = 0; count < counter; count++) {
(function(count) {
displayHotspot('scribble',hotspots[count], count);
count = count + 1;
})(count);
}
}
function loadHotspots() {
fetchHotspotList();
listHotspots();
}
$(document).ready(function() {
hsArea = $("#hotspotArea");
fetchHotspotList();
listHotspots();
});
(Sorry the formatting is a bit off!) - Now, the $(document).ready() function assigns the hsArea variable as it should, however, a combination of fetchHotspotList() and listHotspots() returns:
Uncaught TypeError: Cannot call method 'replace' of null
However, if in the Google Chrome Javascript console, I run:
loadHotspots();
it fetches the data from the AJAX request and displays it properly on the page. At first I thought the problem was that I Wasn't using the $(document).ready() handler, but adding it hasn't fixed it. Neither has using an onload handler inside of the body tag.
Any help would be greatly appreciated.
Regards,
Ben.
It's probably due to the fact that your listHotSpots function is called before fetchHotSpots returns (since it's an async call).
You're better off chaining the execution of listHotSpots to the completion of fetchHotSpots, like so:
function fetchHotspotList() {
$.getJSON ('/alpha/engine/hotspots/gethotspot.php', {'type' : 'list'}, function(json) {
hotspots = json;
listHotSpots();
});
}
You may be better off modifying listHotSpots to take the json data returned from your AJAX call. Hope this helps!
NOTE:
I gave up on trying to do the processing in one go, and just let it return after every x number of sends.
Two paths,
/sms?action=send
/sms?action=status
Let's say that the send path starts sending 10,000 sms messages via REST api calls.
I make a call to that page via ajax.
Then every few seconds, I make a call to /sms?action=status to see how the progress is going, and to update a progress bar.
The status path returns false if no messages are being sent.
What ends up happening is that the ajax call to the SEND path gets the ajax success: function called almost instantly, even though I know the script is taking 1+ minute to complete execution.
My progress bar never gets shown because the status ajax call (which is in a set interval with a few second delay) never seems to actually get called until the send call completes.
I'm trying to put the relevant code in here, but it may not be as clear as it should be without all the context.
<script type="text/javascript">
var smsInterval = 0;
var smsSending = false;
$(document).ready(function() {
var charCount = 0;
var smsText = "";
var smsTotal = <?php echo $options["smsTotal"]; ?>;
<?php if($options["sending"]): ?>
smsStatus();
smsSending = true;
smsInterval = setInterval("smsStatus()", 5000);
<?php endif; ?>
$("span#smsadmin_charcount").html(charCount.toString());
//send button
$("div#smssend").click(function() {
if(smsSending == true) {
return false;
}
smsStatus();
var dataString = $("#smsadmin_form").serialize();
smsSending = true;
$("div#smssend").html("Sending...");
$.ajax({
type: "POST",
url: "<?php echo $base_url; ?>/admin/sms",
data : dataString,
success: function(data) {
},
error: function(request, error) {
$("div.notice.sms").html("ERROR "+error+ "REQUEST "+request);
}
});
});
});
function smsStatus() {
var dataString = "smsaction=status&ajax=true";
$.ajax({
type: "POST",
url: "<?php echo $base_url; ?>/admin/sms",
data : dataString,
success: function(data) {
//data being false here indicates the process finished
if(data == false) {
clearInterval(smsInterval);
var basewidth = $("div.sms_progress_bg").width();
$("div.sms_progress_bar").width(parseInt(basewidth));
$("div.sms_progress_notice").html(parseInt(100) + "% Complete");
smsSending = false;
$("div#smssend").html("Send To <?php echo $options["smsTotal"]; ?> Recipients");
} else {
var pcomplete = parseFloat(data);
$("div.sms_progress_bg").show();
var basewidth = $("div.sms_progress_bg").width();
$("div.sms_progress_bar").width(parseInt(basewidth * pcomplete));
$("div.sms_progress_notice").html(parseInt(pcomplete * 100) + "% Complete");
}
},
error: function(request, error) {
$("div.notice.sms").html("ERROR "+error+ "REQUEST "+request);
}
});
}
I might be missing the point, but inside the $("div#smssend").click you got this line:
smsStatus();
shouldn't it be:
smsInterval = setInterval("smsStatus()", 5000);
and INSIDE the success: function(data) for /admin/sms ?
If the send part is sending out 10k messages, and the status returns true if currently sending a message, and false if in between sending, then you have a design issue.
For example, what is status supposed to be showing?
If status is to show how many of a certain block have been sent, then what you can do is to submit the message to be sent (or addresses), and get back some id for that block.
Then, when you ask for a status, pass the id, and your server can determine how many of that group has been sent, and return back the number that were successful, and unsuccessful, and how many are still pending. If you want to get fancy, you can also give an indication how much longer it may be before finishing, based on how many other requests are also pending.
But, how you approach this really depends on what you expect when you ask for the status.