I would like to set up a progressbar showing the progress of a long working task which imports a large CSV-File and pass to the database. I start the import process with an initial jQuery.ajax call and setting up a timeout to get the processed lines from these file in percent.
The problem is when I start the initial ajax-call, all other ajax-calls just wait to be executed until the initial call is done.
So this is my code:
var progress = false;
var update_progress = function() {
if(progress) {
$.ajax({
url: 'index.php?do=update_progress'
},
function(json) {
// Something < 100
if(json.perc !== undefined) {
$('#progress').css('width', json.perc + '%');
}
setTimeout(update_progress, 1000);
});
}
}
var start_import = function(i) {
// Setting progress allowed
progress = true;
// start the update in 1s
setTimeout(update_progress, 1000);
// start the database-import (20-30 seconds runtime on server)
$.ajax({
url: 'index.php?do=start_import'
},
function(json) {
// Import finished, disallow progressing
progress = false;
// Finally always complete: json.perc is 100
if(json.perc !== undefined) {
$('#progress').css('width', json.perc + '%');
}
});
};
start_import();
This is a bit confusing, because I thought that each call can work itself asynchronously. What is wrong?
Regards Tim
Why do you not call setInterval() instead of setTimeout()? This is the problem! The next call of the function update_progress() will happen after the callback from the previous AJAX call returned!
Related
I have a function that performs an AJAX GET request every 1 second to retrieve data and update a progress bar. I'm using setTimeout to accomplish this. Here is the code:
function check_progress(thread_id) {
function worker() {
$.get('ecab_run/progress/' + thread_id, function(data) {
progress = data['progress'];
status_message = data['status_message'];
if (progress < 100) {
$('.progress-bar').css('width', progress+'%').attr('aria-valuenow', progress);
$('.progress-bar')[0].innerHTML = progress+"%";
$('#status-message')[0].innerHTML = status_message
timer = setTimeout(worker, 1000);
// console.log(timer);
} else if ( progress == 100 ){
$('.progress-bar').css('width', progress+'%').attr('aria-valuenow', progress);
$('.progress-bar')[0].innerHTML = progress+"%";
}
})
return status_message;
}
worker();
}
The function is called after a successful POST as such:
$('#ecab-run-dates').submit(function(e){
e.preventDefault();
var formData = $('form').serialize();
$.ajax({
url:'',
type:'post',
data:formData,
success:function(data){
thread_id = data;
$('.progress-container').show();
check_progress(thread_id);
}
});
});
The backend returns status messages as the behind-the-scenes functions execute, and when it encounters an error, it returns Error: could not compile data or something similar.
My initial thought was to use the error message as a condition for stopping the check_progress function. I've read several answers, including this one that say to use clearTimeout, but I'm not exactly sure how to structure that in my code. Can someone help me exit the setTimeout loop when encountering an error?
I am trying to call an action on a controller with ajax: 10 times with a 2 second delay in my MVC5 application.
Here is the code I've written:
$(document).ready(function () {
(function loop(i) {
setTimeout(function() {
var d = new Date();
console.log(d.getTime());
callAjax();
console.log("works " + i);
if (--i) loop(i);
},
2000); // ms
})(10);
function callAjax() {
$.ajax({
url: '/Home/StartElection',
type: 'POST',
data: "test",
async: true
})
.done(function (partialViewResult) {
$("#partialTable").html(partialViewResult);
});
};
});
The console log is as expected (with a 2 second delay) but the calls to the controller happen instantly - when I set a break in Visual Studio on the controller action the next call after continuing takes 2ms
I can't see why this is happening - can any one help?
Edit: I added a console log of Date.getTime() just before the ajax call & there are 2000 ms between each
You have to change this line alone async: true -> async: false
because the AJAX calls will be made asynchronous if this property is set to true.
And so your ajax calls have no time delay.
Hope this helps.
As far as your client side code is concerned it seems to be working well as far as calls are concerned. With that said here are a few things to consider when dealing with ajax:
1) You have control over the number of times that you can call a remote service but you have no control over the time it will take for that service to respond.
2) As such it is usual good practise for most cases to not make ajax calls in a loop (this somewhat defeats the general purpose of ajax). Rather use the response to each call to then make the next call (but then of course we would need to know exactly what it is you are trying to build to suggest an exact solution).
So the closest thing to what you are looking for using ajax I think would be more of something like this:
$(document).ready(function () {
/*(function loop(i) {
setTimeout(function() {
var d = new Date();
console.log(d.getTime());
callAjax();
console.log("works " + i);
if (--i) loop(i);
},
2000); // ms
})(10);*/
var i=0;
function callAjax() {
var requestTimeMessage = "fetch #"+i+" at: "+new Date().getTime();
console.log(requestMessage);
$.ajax({
url: '/Home/StartElection',
type: 'POST',
data: "test",
async: true
})
.done(function (partialViewResult) {
var requestTimeMessage = "response #"+i+" at: "+new Date().getTime();
console.log(requestMessage);
$("#partialTable").html(partialViewResult);
i++;
if(i<10) {
/*Note this assumes a quick response for each call.
* Otherwise the response together with this delay will total a wait longer than 2 seconds.
*/
setTimeout(function() {
callAjax();
2000);
}
});
};
});
But as I said. I would need to know exactly what you are trying to achieve to give more appropriate answer to your question.
Not sure if I'm phrasing my question right, but I have the code below. Basically want to make an ajax request, check the call back, and re-perform the ajax until it gets the desired response (in the example connectedvoter==1).
The problem is it only takes like 80ms and the amount of xhr's gets to huge numbers really fast at that speed. I tried to come up with a way to 'pause' but everything I could think of ate up cpu.
Is there a way to 'slow down' the amount of requests made, to say once a second or two without eating up cpu?
var connctedvoter = 0;
var govoters = function () {
$.ajaxSetup({
async: false
});
var url = "getconnectedvoter.php";
var data = {
userid: userid
};
$.getJSON(url, data, callback);
};
var pausevoters = function () {
console.log("pausing ajax voters");
};
var callback = function (response) {
if (response.error) {
return;
}
if (response.connectedvoter == 0) {
setTimeout(govoters, 150);
//govoters();
} else {
$('#vanid').html(response.vanid);
$('#name').html(response.name);
$("#mapurl").attr("src", response.mapurl);
$('.call').fadeIn();
return;
}
};
//DO THIS TO START
setTimeout(govoters, 150);
pausevoters();
Just increase the delay of the timeout:
if(!response.connectedvoter) {
setTimeout(govoters, 2000); // instead of 150
}
Instead of 10 requests for a second:
setTimeout(govoters, 150); // one request every 150 millisecond.
Note that for start you can change from this:
//DO THIS TO START
setTimeout(govoters, 150);
To this:
//DO THIS TO START
govoters();
There isn't really a need for a timeout here.
Just change lines with this code:
setTimeout(govoters, 150);
with:
setTimeout(govoters, 1000); // 1 second
I have a LoadingStatus Function that has two options SHOW or HIDE.
The Show triggers to display when the JQUERY POST is made, the HIDE happens after the RESPONSE comes back.
The issue I'm having is that sometimes this happens so fast that it makes for a bad experience. What I thought about doing was putting in a JavaScript PAUSE, but if the POST takes a while to respond it will take even longer because of the PAUSE.
How can I have my SHOW HIDE function work together, to make sure at minimum the SHOW was displayed to the user for at least 1/2 second?
function saveBanner (action) {
if (action == 'show') {
// Display the AJAX Status MSG
$("#ajaxstatus").css("display","block");
$("#msg").text('Saving...');
}
else if (action == 'hide') {
$("#ajaxstatus").css("display","none");
$("#msg").text('');
}
};
Thanks
In your ajax success callback, you can put the hide command in a setTimeout() for 1500 miliseconds:
success: function(results) {
setTimeout(function(){
saveBanner("hide");
}, 1500);
}
Of course that would merely add 1.5 seconds onto however long the process itself took. Another solution would be to record the time the process started, with the Date object. Then, when the callback takes place, record that time and find the difference. If it's less than a second and a half, set the timeout for the difference.
/* untested */
var start = new Date();
success: function(results) {
var stop = new Date();
var difference = stop.getTime() - start.getTime();
difference = (difference > 1500) ? difference : 1500 ;
setTimeout(function(){
saveBanner("hide");
}, difference);
}
You can perform this math either inside your callback, or within the saveBanner() function itself, within the show portion you would set the starting time, within the hide() portion you would check the difference and set the setTimeout().
You can use setTimeout/clearTimeout to only show the status when the response takes longer than a set amount of time to load.
Edit:
Some untested code:
var t_id = 0;
function on_request_start()
{
t_id = setTimeout(show_message, 1000);
}
function on_request_completed()
{
clearTimeout(t_id);
hide_message();
}
The JQuery handlers should look something like the above. The message will not be shown if you receive a reply in less than a second.
var shownTime;
function saveBanner (action) {
if (action == 'show') {
// Display the AJAX Status MSG
$("#ajaxstatus").css("display","block");
$("#msg").text('Saving...');
shownTime = new Date().getTime();
}
else if (action == 'hide') {
var hideIt = function() {
$("#ajaxstatus").css("display","none");
$("#msg").text('');
};
var timeRemaining = new Date().getTime() - shownTime - 1500;
if (timeRemaining > 0) {
setTimeout(hideIt, timeRemaining);
else {
hideIt();
}
}
};
As of jQuery 1.5, you are able to extend the $.ajax functionality by using prefilters. I wanted a similar experience where a message was shown a minimum amount of time when an ajax call is made.
By using prefilters, I can now add a property to the ajax call named "delayedSuccess" and pass it a time in milliseconds. The time that is passed in is the minimum amount of time the ajax call will wait to call the success function. For instance, if you passed in 3000 (3 seconds) and the actual ajax call took 1.3 seconds, the success function would be delayed 1.7 seconds. If the original ajax call lasted more than 3 seconds, the success function would be called immediately.
Here is how I achieved that with an ajax prefilter.
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (originalOptions.delaySuccess && $.isFunction(originalOptions.success)) {
var start, stop;
options.beforeSend = function () {
start = new Date().getTime();
if ($.isFunction(originalOptions.beforeSend))
originalOptions.beforeSend();
};
options.success = function (response) {
var that = this, args = arguments;
stop = new Date().getTime();
function applySuccess() {
originalOptions.success.apply(that, args);
}
var difference = originalOptions.delaySuccess - (stop - start);
if (difference > 0)
setTimeout(applySuccess, difference);
else
applySuccess();
};
}
});
I first check to see if the delaySuccess and success options are set. If they are, I then override the beforeSend callback in order set the start variable to the current time. I then override the success function to grab the time after the ajax call has finish and subtract the difference from the original delaySuccess time. Finally, a timeout is set to the computed time which then calls the original success function.
I found this to be a nice way to achieve this effect and it can easily be used multiple times throughout a site.
I'm a novice-to-intermediate JavaScript/jQuery programmer, so concrete/executable examples would be very much appreciated.
My project requires using AJAX to poll a URL that returns JSON containing either content to be added to the DOM, or a message { "status" : "pending" } that indicates that the backend is still working on generating a JSON response with the content. The idea is that the first request to the URL triggers the backend to start building a JSON response (which is then cached), and subsequent calls check to see if this JSON is ready (in which case it's provided).
In my script, I need to poll this URL at 15-second intervals up to 1:30 mins., and do the following:
If the AJAX request results in an error, terminate the script.
If the AJAX request results in success, and the JSON content contains { "status" : "pending" }, continue polling.
If the AJAX request results in success, and the JSON content contains usable content (i.e. any valid response other than { "status" : "pending" }), then display that content, stop polling and terminate the script.
I've tried a few approaches with limited success, but I get the sense that they're all messier than they need to be. Here's a skeletal function I've used with success to make a single AJAX request at a time, which does its job if I get usable content from the JSON response:
// make the AJAX request
function ajax_request() {
$.ajax({
url: JSON_URL, // JSON_URL is a global variable
dataType: 'json',
error: function(xhr_data) {
// terminate the script
},
success: function(xhr_data) {
if (xhr_data.status == 'pending') {
// continue polling
} else {
success(xhr_data);
}
},
contentType: 'application/json'
});
}
However, this function currently does nothing unless it receives a valid JSON response containing usable content.
I'm not sure what to do on the lines that are just comments. I suspect that another function should handle the polling, and call ajax_request() as needed, but I don't know the most elegant way for ajax_request() to communicate its results back to the polling function so that it can respond appropriately.
Any help is very much appreciated! Please let me know if I can provide any more information. Thanks!
You could use a simple timeout to recursively call ajax_request.
success: function(xhr_data) {
console.log(xhr_data);
if (xhr_data.status == 'pending') {
setTimeout(function() { ajax_request(); }, 15000); // wait 15 seconds than call ajax request again
} else {
success(xhr_data);
}
}
Stick a counter check around that line and you've got a max number of polls.
if (xhr_data.status == 'pending') {
if (cnt < 6) {
cnt++;
setTimeout(function() { ajax_request(); }, 15000); // wait 15 seconds than call ajax request again
}
}
You don't need to do anything in your error function unless you want to put an alert up or something. the simple fact that it error will prevent the success function from being called and possibly triggering another poll.
thank you very much for the function. It is a little bit buggy, but here is the fix. roosteronacid's answer doesn't stop after reaching the 100%, because there is wrong usage of the clearInterval function.
Here is a working function:
$(function ()
{
var statusElement = $("#status");
// this function will run each 1000 ms until stopped with clearInterval()
var i = setInterval(function ()
{
$.ajax(
{
success: function (json)
{
// progress from 1-100
statusElement.text(json.progress + "%");
// when the worker process is done (reached 100%), stop execution
if (json.progress == 100) clearInterval(i);
},
error: function ()
{
// on error, stop execution
clearInterval(i);
}
});
}, 1000);
});
The clearInterval() function is becomming the interval id as parameter and then everything is fine ;-)
Cheers
Nik
Off the top of my head:
$(function ()
{
// reference cache to speed up the process of querying for the status element
var statusElement = $("#status");
// this function will run each 1000 ms until stopped with clearInterval()
var i = setInterval(function ()
{
$.ajax(
{
success: function (json)
{
// progress from 1-100
statusElement.text(json.progress + "%");
// when the worker process is done (reached 100%), stop execution
if (json.progress == 100) i.clearInterval();
},
error: function ()
{
// on error, stop execution
i.clearInterval();
}
});
}, 1000);
});
You can use javascript setInterval function to load the contents each and every 5 sec.
var auto= $('#content'), refreshed_content;
refreshed_content = setInterval(function(){
auto.fadeOut('slow').load("result.php).fadeIn("slow");},
3000);
For your reference-
Auto refresh div content every 3 sec