Ajax队列不提供任务

时间:2013-09-22 09:25:59

标签: jquery asp.net-mvc asp.net-mvc-4 queue

我有一个ajax代码,它工作正常:

       $.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/>');
           }
       });

这个问题是它是异步的,所以视图控件中的消息显示无序,所以我决定在ajax中放入选项async:false但是这会导致Web应用程序被完全阻止,但是消息显示是有序的...糟糕的想法,所以我想在实现一个队列来提供ajax调用而不使用选项async:false。我用Google搜索,我发现了这个:

How can jQuery deferred be used?

在上面的代码下面:

/* 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();
    };
};

看起来很有希望,所以我决定尝试一下,所以我修改了我的ajax代码并在结果下面,注意我已经替换了内部ajax块:

       $.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/>');
           }
       });

我不确定如果我正确使用缓冲区的调用,肯定不是因为我在控制器中的操作DoTask中放了一个断点而且从不停止所以我没有正确排队每个任务,调用Buffer似乎不正确....那么我做错了什么?

首次尝试(来自Paul Grime的解决方案): 我已经完成了你的解决方案,但我正在尝试修改一些我无法做到的事情:

1)我的DoTask返回一个http代码200(如果任务完成正常)或500(如果任务没有完成正常)HttpStatusCodeResult(HttpStatusCode.OK)/ HttpStatusCodeResult(HttpStatusCode.NotFound)所以在显示的字符串中(该字符串开始完成...)我想添加doTask的结果,例如:

如果doTask完成了任务,那么:

“结果”:“通过”=&gt;完成{“结果”:“通过”,......}

如果doTask没有正确完成任务:

“结果”:“失败”=&gt;完成{“结果”:“失败”,......}

2)我已经对我的任务进行了分组,所以首先我开始执行一种任务,然后当这些任务完成时,如果它们没有问题就独立,我需要启动下一类要完成的任务,并且那么...如何修改你的代码呢?

第二次尝试:

控制器:

[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);
}

查看:

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"));
    });
}

由于某种原因,在尝试打印“收到的GetData结果...”时,第一部分(“状态”,“确定”,“操作”,“getTasks”)被遗漏,只有最后一部分,与“数据”相关“打印(显示)。

1 个答案:

答案 0 :(得分:1)

This jsfiddle (jQuery 1.10.1)this jsfiddle (jQuery 1.7.2)可能会帮助您入门(差异是1.10.1版本使用Deferred.then()而1.7.2版本使用Deferred.pipe()

当我使用deferreds / promises时,我试图寻找的是我减少了callback hell,或者更简单地说,降低了嵌套异步回调引入的嵌套级别。

首先,从识别逻辑功能开始,然后将其重构为命名良好的函数,每个函数都返回延迟。

function getTasks() {
    return ajax({
        // replace original URL with jsfiddle URL and test data
        //url: "/Tests/GetData/",
        url: "/echo/json/",
        data: jsFiddleData(fakeTasks),

        type: 'POST',
        dataType: 'json',
        contentType: "application/json; charset=utf-8"
    }).then(function(data) {
        return ok(createObject("status", "ok", "op", "getTasks", "data", data));
    }, function() {
        return ok(createObject("status", "fail", "op", "getTasks"));
    });
}

function doTask(task) {
    return ajax({
        // replace original URL with jsfiddle URL and test data
        //url: "/Tests/DoTask/",
        //data: {
        //    taskName: task.TaskName
        //},

        //  + "?" + task.TaskName for cache-busting
        url: "/echo/json/" + "?" + task.TaskName,
        data: jsFiddleData({
            "status": "doing " + task.TaskName
        }),

        type: 'POST',
        dataType: 'json'
    }).then(function(data) {
        return ok(createObject("status", "ok", "op", "doTask", "task", task, "data", data));
    }, function() {
        return ok(createObject("status", "fail", "op", "doTask", "task", task));
    });
}

function doTasks(tasks) {
    // Create a deferred for each task by calling doTask().
    var deferreds = $(tasks).map(function (i, task) {
        postStatus("Sending DoTask request: " + i + "," + JSON.stringify(task));
        return doTask(task);
    }).toArray();

    // return a composite deferred which will
    // wait for each of the doTask requests.
    return $.when.apply($, deferreds);
}

您的应用代码最终会看起来像:

getTasks().then(function (tasks) {
    postStatus("Received GetData results");
    return doTasks(tasks);
}).then(function (results) {
    postStatus("Received DoTask results");
    for (var i = 0; i < results.length; i++) {
        postStatus('done ' + JSON.stringify(results[i]));
    }
}).fail(function (err) {
    postStatus("Error: " + JSON.stringify(err));
});

事实上,如果没有console.log的某些内容,并且通过一些函数重构,它可以更好地读取:

function showResults(results) {
    postStatus("Received DoTask results");
    for (var i = 0; i < results.length; i++) {
        postStatus('done ' + JSON.stringify(results[i]));
    }
}

getTasks().then(doTasks).then(showResults).fail(function (err) {
    postStatus("Error: " + JSON.stringify(err));
});