the below code if the ajax call i make to my run.py and then displays the output in a html tag. the script run.py runs for over 2 mins. But in the js script below. as soon as the script has begun to run. the output ( initial lines of the output) will be displayed in the html tag. the remaining part of the script won't be displayed.
$(document).ready(function(){
$(sub).click(function(){
alert("connecting to host")
$.ajax({
type:'POST',
url:'/cgi-bin/run.py',
dataType: 'html',
success:function (z) {
$('#output').html(z);
}
});
}) ;
});
I'd like to know if there is any function in ajax to wait till completion of script (Not just execution of script. but wait till end ) and then display entire output to the html tag.
Here is my python script:
import sys, os
import cgi, cgitb
def __init__(self, address, username, password):
# connects to host
def sendShell(self, command):
#opens shell
def process(self):
while self.shell.recv_ready():
info += self.shell.recv(1024)
output = str(info, "utf8")
print(output)
hostname = "z.com"
password = "yyy"
username = "dd"
connection = ssh(hostname, username, password)
connection.openShell()
connection.sendShell("date");
jQuery.ajax() has an option async: false, however I'd advise against it and just do whatever you need in ajax callbacks. Also, it's deprecated.
Changing the async flag of the $.Ajax function to false is almost correct, yet in your case the script needs to run for long time so you need to consider using Long Polling for such a request.
The reason is because the browsers have max timeout for ajax calls and usually is set to 1 min depending on the browser(so in your case after 1 min the client / browser stops the connection and wants the response, but you want it to wait until its done and only then send the response back).
So to overcome this you will have to send once a 20 sec or once of the max timeout another request to the py script to check if its done.
Code snippet for javascript side:
function isDone(timestamp) {
timestamp = timestamp || null;
$.ajax({
type:'POST',
url:'/cgi-bin/run.py',
dataType: 'json',
data: { "timestamp": timestamp },
timeout: 20000, // Or to whatever the max-timeout is, haven't checked that with jQuery.
success: function (response) {
if (response.done === false) {
isDone(Date.now ());
} else {
// got the results, can continue.
$('#output').html(response.output);
}
}
});
}
isDone();
I'm not sure how the pyton script should look like, if you want you can share it with me and I will try to complete the server side.
Basically what you should do there is to set the timeout of the script to the max, and return the correct response to the client.
JSON response should look like this:
{
"done": true, // or false if the script timed out.
"output": html, // the variable that should contain the output when the py script is done, if its not done just send a null or don't send it back to the client at all.
"timestamp": time.time() // if im not wrong that's how you get timestamp in py
}
Server-side in pseudo code:
Dynamcally or under setting, configure python script execution time to max or rather to 3 min, sense you mentioned it takes 2 min.
if (timestamp === null) { // first ajax call to this script.
- Start long processing task.
- Write the output to a file.
}
do { // check this code block every 1 sec
if (the file you are writing to was created and is complete / unlocked) {
- Read from that file the content, save it into a variable and delete the file from your system.
- Output the above mentioned response with done set to true and the current timestamp.
- Break outside of the loop
}
if (timed out) { // basically check if 20 second passed sense starting timestamp.
- Output the above mentioned response with done set to false and the current timestamp.
- Break outside of the loop.
}
sleep for 1 sec, you don't want to kill your CPU usage.
} while (true)
Related
I have a webpage that calls an AJAX script - ajax.php. I am using JQuery to send the AJAX requests.
The script ajax.php receives some arguments via$_REQUEST, based on that it starts its processing. The processing has multiple steps involved and at the end of the each step I want to send some feedback back to the page - for example:
Step 1 completed
Step 2 completed
....
Once all the steps are completed - the script ajax.php will output a TXT file which I am outputting via:
header('Content-type: text/plain');
header('Content-Disposition: attachment; filename="output.txt"');
My questions are:
I have a div in the page where I want to show the user that Step 1 completed, Step 2 completed, ... If I use JQuery.ajax(), will the .done function called multiple times? If no, whats the best way to handle it? Can I use ob_implicit_flush in PHP to send 'Step x completed 'messages?
Finally, how will I handle the output of .txt file so that user's browser downloads it? I don't want to save the file on the server and then going into hassle of server disk space, cron jobs of deletes, etc.
I have the option of doing multiple AJAX requests - but again I don't want to do this as this will make my code logic pretty complex and I will have to save a lot of data in $_SESSION to be visible across requests which is again something that I don't want to do.
After your AJAX call to kick off your process, you could make another AJAX call in a loop which requests, returns, and presents the current percentage complete until it reaches 100%. Basically, one AJAX call to initiate the process and then a series of calls which check status.
Here is some simple JavaScript to achieve what you want:
<script>
function startProcess() {
//start your long-running process
$.ajax({
type: 'GET',
url: "/longRunningProcess",
async: true,
success:function (data) {
//do something - your long process is finished
}
});
}
function getStatus() {
//check your progress
$.ajax({
type: 'GET',
url: "/checkProgress",
async: true,
success:function (data) {
//assume the data returned in the percentage complete
var percentage = parseInt(data);
//write your status somewhere, like a jQuery progress bar?
if (percentage < 100) {
//if not complete, check again
getStatus();
}
}
});
}
</script>
I have a pyramid application that runs perfectly on a local server, but when I move it over to a web server (Dreamhost), I get the following error:
400 Bad Request:
Bad request (GET and HEAD requests may not contain a request body)
The code in question is the following ajax in Javascript:
function summary_ajax(sName){
$.ajax({
type: "POST",
url: "summary",
dataType: "json",
data: {
'ccg_name': sName,
},
async: false,
success: function(data) {
//alert("In ajax success function") <----------- This never executes
lValues = data.lValues;
lLabels = data.lLabels;
},
});
};
return (lValues, lLabels);
And is handled in views.py:
#view_config(route_name="ccg_map_summary_ajax",renderer="json")
def ccg_map_summary_ajax(self):
sCCG = self.request.POST.get('ccg_name')
fData = open('pyramidapp/static/view_specific_js/ajax_summary_data.js')
dData = json.load(fData)
lLabels = dData[sCCG].keys()
lValues = dData[sCCG].values()
return {
'lLabels' : lLabels,
'lValues' : lValues,
}
I did some testing by placing alert() functions (its slow, because the server only reloads the script every so many minutes), and everything executes fine except for alerts in the ajax call. So it seems that either the post fails, or something goes wrong in the view. Any ideas?
So is there something in this code that works in my local server (in Pyramid) but breaks down in the web server (Dreamhost)?
The file structure is the same in the local and web server. I don't see why it shouldn't, but will fData still open the file for reading?
For anyone else out there, I found the problem:
The path I specified above was a relative path that worked on my system but not on the server because the working directories are obviously different. So instead of using a relative path, I just changed the script to have the correct absolute path.
To find the current working directory path, just enter pwd into terminal.
I'm following the example below to continuously refresh a div with a mysql table.
http://techoctave.com/c7/posts/60-simple-long-polling-example-with-javascript-and-jquery
I'm using the complete and timeout parameter of ajax to refresh the div instead of using setinterval and settimeout.
The problem I'm having is that the returning data can include links and these are not working when clicked. I believe the problem could be that the div is constantly refreshing and thus I the click is ignored. How do you allow links within a refreshing div? It works with setinveral and settimeout but I want to use long polling to allow real time updates.
Here is my code.
// get page url variables
function getUrlVars() {
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
vars[key] = value;
});
return vars;
}
// set var for parent id to scroll to
var tid = getUrlVars()["tid"];
var pid = getUrlVars()["pid"];
(function poll(){
// get latest page
$.ajax({
url: "ajax.tickets_details.php?tid=" + tid,
type: 'GET',
cache: false,
success: function(html) {
// print results from get in div
$("#ticket_updates").html( html );
},
complete: poll,
timeout: 30000
});
})();
Thanks!
I've just read that tutorial and it's based on false information.
this tutorial says:
This means our poll function won't get called again until both the
ajax call is complete and (at-least) thirty (30) seconds have passed.
this isn't true. If your request returns in < 30s it will fire again immediately, thus causing your problem. The Actual definition of timeout is:
Set a timeout (in milliseconds) for the request. This will override
any global timeout set with $.ajaxSetup(). The timeout period starts
at the point the $.ajax call is made; if several other requests are in
progress and the browser has no connections available, it is possible
for a request to time out before it can be sent. In jQuery 1.4.x and
below, the XMLHttpRequest object will be in an invalid state if the
request times out; accessing any object members may throw an
exception. In Firefox 3.0+ only, script and JSONP requests cannot be
cancelled by a timeout; the script will run even if it arrives after
the timeout period.
So this means if the request takes more than 30s it will cancel the waiting handler only (not the call itself, this will continue but the javascript handler will go out of scope).
There is even a comment highlighting this flaw:
I'd find a new tutorial as that one appears to be talking nonesense. This technique is not doing "server push" at all. Only web sockets can push from the server. HTTP 1.1 does not support any server push methods at all.
I have an ajax call that is grabbing a large json list. Is there any way I can make a progress bar that gets the real value of json load (for example a status bar that says 1 out of 200 loaded)?
Right now I have a pretty basic Ajax call
function SendAjax(urlMethod, jsonData, returnFunction) {
$.ajax({
type: "GET",
contentType: "application/json; charset=utf-8",
url: urlMethod,
data: jsonData,
dataType: "json",
success: function (msg) {
if (msg != null) {
ReturnJson(msg);
}
},
error: function (xhr, status, error) {
// Boil the ASP.NET AJAX error down to JSON.
var err = eval("(" + xhr.responseText + ")");
// Display the specific error raised by the server
alert(err.Message);
}
});
}
Try using AjaxStart on your application global scope. That means you can put the code in your layout file, and if the processing is long, it will show the progress indicator...
$(document).ajaxStart(function() {
$( "#loading" ).show();
});
You can see the example and answer at preload with percentage - javascript/jquery.
there are a few states in an ajax request, but they do not represent any percentage through the request.
The network traffic should really be your real problem, you do not want to send 200 separate requests (although this would allow for a progress bar, it would make the whole request take significantly longer, depending on your network connection to the server).
You are best off just showing an activity indicator and removing it when the request completes and try to optimise your code server side to return the 200 items as fast as possible.
If 200 items really is too big (larger than X seconds to return them), you could split it in half or quarters to show some progress however this will waste time with those extra requests on network, page headers, etc.
If your server-side code has a way of sharing application state (such as the $_SESSION in PHP) you could make 2 separate requests, one that asks for the data, and one that checks on progress of the first request. Repeat the second request on a timer until the first completes, and update the $_SESSION (or whatever works in your server code) as each item is processed.
For example:
The initial page must start a session, so that the subsequent AJAX calls have the cookie, and can access the shared data:
<?php
session_start();
session_write_close(); // close the session so other scripts can access the file (doesn't end the session)
// your page content here
?>
First AJAX Call to start the processing:
<?php
function updateSession($count){
session_start(); // open the session file
$_SESSION['progress'] = $count;
session_write_close(); // let other requests access the session
}
// as you process each item, call the above function, ex:
for ($i = 1; $i <= 10; $i++) {
updateSession($i);
}
?>
Second AJAX call (repeated every X seconds) looks like:
<?php
session_start(); // open the session file
echo #$_SESSION['progress'] or 0; // echo contents or 0 if not defined
session_write_close(); // let other requests access the session
?>
Sorry I don't know ASP.NET, but hopefully the above code is useful to you.
Can someone explain what this ajax code does?
function ajaxProgress(){
//Math.random() is for bitchy ie to prevent caching the xml.
$.get('sample.ff?do=progressInfo&type=sampletype&dummy='+Math.random(), { dataType: 'xml'}, function(xml) {
//if the import is running get infos about the progress...
if($('/importProgress/running', xml).text() == 'true') {
//..there are no infos yet, so it was just started..
if($('/importProgress/progress', xml) == null || $('/importProgress/progress', xml).text() == ''){
//do something
}
..........
setTimeout( "ajaxProgress()", 1000);
This function is calling itself recursively every second. It sends an AJAX GET request to Import.ff and passing 3 query string parameters: do=progressInfo, type=sampletype and a random number. This random number is appended to the url because GET requests are cached by browsers and by this it ensures that it gets fresh content from the server on each request.
The server itself sends an XML file as response. This XML file contains some nodes like:
<importProgress>
<running>true</running>
<progress>20</progress>
</importProgress>
So the script parses this XML in the success callback of the AJAX request. It tries to get the values of the running and progress nodes. If running=true then it checks whether there's a progress node and does some processing with it. Finally it calls itself 1 second later using the setTimeout function. And so on.
So basically this script reports progress from some server operation by polling the server at 1 seconds intervals using AJAX GET requests and parsing the response.