I have below ajax code and it is working ok:
$.ajax({
//async: false,
url: "/Tests/GetData/",
type: 'POST',
dataType: 'json',
contentType: "application/json; charset=utf-8",
success: function (data) {
$.each(data, function (i, item) {
$.ajax({
//async: false,
url: "/Tests/DoTask/",
type: 'POST',
data: { taskName: item.TaskName },
success: function () {
$("#Status").append('Task PASSED.<br/>');
},
error: function () {
$("#Status").append('Task FAILED!<br/>');
},
beforeSend: function () {
$("#Status").append('Doing task...<br/>');
}
});
});
$("#Status").append('Process completed.</span><br/>');
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
$("#Status").append('Error: ' + errorThrown + '<br/>');
},
beforeSend: function () {
$("#Status").append('<br/>Process started.<br/>');
}
});
The problem with this is that it is asynchronous so the messages in the view control are being displayed unordered, so I decided to put within ajax the option async:false but this cause web application get completely blocked althought the messages are displayed ordered...bad idea so I thought in implementing a queue to serve the ajax calls and not use the option async:false. I googled and I found this one:
How can jQuery deferred be used?
Below the code from the above page:
/* Class: Buffer
* methods: append
*
* Constructor: takes a function which will be the task handler to be called
*
* .append appends a task to the buffer. Buffer will only call a task when the
* previous task has finished
*/
var Buffer = function(handler) {
var tasks = [];
// empty resolved deferred object
var deferred = $.when();
// handle the next object
function handleNextTask() {
// if the current deferred task has resolved and there are more tasks
if (deferred.isResolved() && tasks.length > 0) {
// grab a task
var task = tasks.shift();
// set the deferred to be deferred returned from the handler
deferred = handler(task);
// if its not a deferred object then set it to be an empty deferred object
if (!(deferred && deferred.promise)) {
deferred = $.when();
}
// if we have tasks left then handle the next one when the current one
// is done.
if (tasks.length > 0) {
deferred.done(handleNextTask);
}
}
}
// appends a task.
this.append = function(task) {
// add to the array
tasks.push(task);
// handle the next task
handleNextTask();
};
};
It seems promising so I have decided to try it, so I have modified my ajax code and below the result, note that i have replace the inner ajax block:
$.ajax({
//async: false,
url: "/Tests/GetData/",
type: 'POST',
dataType: 'json',
contentType: "application/json; charset=utf-8",
success: function (data) {
$.each(data, function (i, item) {
Buffer({
//async: false,
url: "/Tests/DoTask/",
type: 'POST',
data: { taskName: item.TaskName },
success: function () {
$("#Status").append('Task done.<br/>');
},
error: function () {
$("#Status").append('Task failed!<br/>');
},
beforeSend: function () {
$("#Status").append('Doing task...<br/>');
}
});
});
$("#Status").append('Process completed.</span><br/>');
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
$("#Status").append('Error: ' + errorThrown + '<br/>');
},
beforeSend: function () {
$("#Status").append('<br/>Process started.<br/>');
}
});
I am not sure If I am using correctly the call to the buffer, sure not because i have put a breakpoint in my action DoTask in the controller and never stops so I am not queuing correctly each task, call to Buffer seems incorrect.... So what am i doing wrong?
First attempt (Solution from Paul Grime): I have done your solution, but I am trying to modify some things that i am not able to do:
1) My DoTask returns an http code 200 (if task done ok) or 500 (if task not done ok) HttpStatusCodeResult(HttpStatusCode.OK)/HttpStatusCodeResult(HttpStatusCode.NotFound) so in the string displayed (that string that begins with done ...) I want to add the result of the doTask, for example:
if doTask has done the task ok:
"Result" : "Passed" => done { "Result" : "Passed", ...}
If doTask hasn't finished the task correctly:
"Result" : "Failed" => done { "Result" : "Failed", ...}
2) I have grouped my tasks, so first I launch to do a type of tasks, then when those has finished, independenty if they go ok or not, I need to launch the next type of tasks to be done, and so on... How to modify your code to do it?
Second attempt:
Controller:
[HttpPost]
public JsonResult GetData()
{
var data = (dynamic)null;
using (BBDDContext context = new BBDDContext())
{
data = context.MyObject.Where(o => o.TypeId == 1).OrderBy(k => k.Name).Select(obj => new
{
name =obj.Name,
description =obj.Description
}).ToList();
}
return Json(data, JsonRequestBehavior.AllowGet);
}
View:
function getTasks() {
return ajax({
url: "/Tests/GetData/",
type: 'POST',
dataType: 'json',
contentType: "application/json; charset=utf-8"
}).then(function (data) {
// data contains a list of pairs [Name IP]
return ok(createObject("status", "ok", "op", "getTasks", "data", JSON.stringify(data)));
}, function () {
return ok(createObject("status", "fail", "op", "getTasks"));
});
}
for some reason when trying to print "Received GetData results..." the first part ("status", "ok", "op", "getTasks") is missed, only last one, that related to "data" is printed (displayed).