javascript - 在绘制图表之前在每个循环中编排嵌套的 AJAX 调用 (JQuery)

标签 javascript jquery ajax morris.js

几天来我一直在寻找这个问题的答案,但运气不佳。

我正在尝试根据通过对 JSON REST Api 的 ajax 调用收集的数据构建图表。我使用 morris.js 来构建我的图表。图表的数据是通过 3 个嵌套的 ajax 调用收集的。第一个 ajax 调用检索包含任务的列表,成功时循环迭代结果并对每个结果进行 ajax 调用。并在这些调用成功时检索数据以构建图表。

在最后一个成功函数中,for 循环 运行以构建一个包含图表数据的数组。

这些数据被插入图表的参数数组 (morris.js)。

所以 3 个嵌套的 ajax 调用,在第一个调用之后有一个 $.each 循环。

我的问题是每次 ajax 迭代都会构建图表。我一直在尝试将图表函数 (Morris.Donut(params);) 移出循环,但没有成功。

我的代码:

var arr = [];
var counts = [];
var test = [];
$.ajax({
    url: "http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests?$filter=TaskStatusValue%20eq%20%27Not%20Started%27",
    headers: {
        'accept': 'application/json;odata=verbose',
        'content-type': 'application/json;odata=verbose'
    },
    success: function(data) {
        $.each(data.d.results, function(a, data) {
            $.ajax({
                url: "http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests(" + data.RequesterId + ")/CreatedBy",
                headers: {
                    'accept': 'application/json;odata=verbose',
                    'content-type': 'application/json;odata=verbose'
                },
                success: function(data2) {
                    $.ajax({
                        url: "http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests(" + data.AssignedToId + ")/AssignedTo",
                        headers: {
                            'accept': 'application/json;odata=verbose',
                            'content-type': 'application/json;odata=verbose'
                        },
                        success: function(data3) {
                            var params = {
                                element: 'taskchart',
                                data: [],
                                colors: ['#b85f28', '#d46125', '#CE4E00']
                            };
                            $(".inner").prepend('<p>'+data.Request +' <br>Submitted by: '+data2.d.Name+'<br>Assigned to: '+data3.d.Name+' | Due in: '+data.DueInDays+' day(s)</p>');
                            var indexOfName = arr.indexOf(data3.d.Name);
                            if (indexOfName == -1) {
                                arr.push(data3.d.Name);
                                counts.push(1);
                            } else {
                                counts[indexOfName] ++;
                            }
                            for (var i in arr, counts) {
                                test = {
                                    'label': '' + arr[i] + '',
                                    'value': counts[i]
                                }
                                params.data.push(test);
                            }
                            Morris.Donut(params);
                        }
                    })
                }
            })
        })
    }
})

我知道这段代码看起来有点乱,但我似乎无法将其构建得更好。关于在 ajax 调用完成加载数据后如何构建图表的任何建议?

我真的要用头撞墙了。

最佳答案

问题的快速分析

在更仔细地查看您的代码后,您的图表在 $.each 函数中的每次迭代都重新构建的原因是因为您的 Morris.Donut(params); 在循环中最深的嵌套 success 回调中被调用(duh?)。将 Morris.Donut(params); 移到 $.each 函数之外将无法正常工作,因为嵌套的 ajax 调用的异步性质,即调用在 ajax 调用完成之前,$.each 可能会退出并调用 Morris.Donut(params);

更简单的备用(服务器端)解决方案

在向您展示一些 javascript 代码之前,我建议如果您可以控制服务器端 API 代码,您应该考虑将所有这些复杂性移到那里;即创建一个接受 TaskStatusValue 输入的新 Web 服务,并返回满足此条件的 IT 请求以及构建图表所需的任何其他信息(请求者姓名、受让人姓名等)。我认为本质上,这就是您的 JS 代码试图做的事情。

正如我在之前的评论中提到的,您的嵌套 AJAX 调用依赖于父 AJAX 调用的结果,这基本上归结为发出同步请求,从而违背了异步调用的目的。

JQuery 解决方案 (TL;DR)

话虽如此,如果您绝对必须在客户端做所有事情,我们将需要传递一个 jQuery.deferred 数组。反对 jQuery.when函数,以便您的图表仅在所有异步调用完成后绘制。

可以有其他更简单、更好的方法来做到这一点。但这是我的解决方案....

代码重构

将两个嵌套的 AJAX 调用提取到一个单独的函数中,如下所示。此函数将返回 $.ajax 函数返回的 Deferred 对象:

function retrieveITRequestsFrom(url){
  return $.ajax(url, {
           headers: {
             'accept': 'application/json;odata=verbose',
             'content-type': 'application/json;odata=verbose'
           },
           /* etc */
         });
}

在您的第一个 success 回调中使用 TaskStatusValue=NotStarted 获取所有 IT 请求后,将您稍后将进行的所有 AJAX(延迟对象)调用添加到一个数组中像这样:

deferreds = []
$.each(data.results, function(){
    var requesterURL = 'http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests(' + this.RequesterId + ')/CreatedBy';
    deferreds.push(retrieveITRequestsFrom(requesterURL));

    var assigneeURL = 'http://helpdesk.site.com/IT/_vti_bin/ListData.svc/ITHelpdeskRequests(' + this.AssignedToId + ')/AssignedTo';
    deferreds.push(retrieveITRequestsFrom(assigneeURL));        
});

最后,您可以将这个 Deferred 对象数组传递给 $.when 函数。

$.when.apply($, deferreds)
 .then(function(){
     $.each(arguments, function(){
         console.log(this[0]);
     });
})

伪数组arguments $.each 函数中应包含从所有 ajax 调用中收集的所有结果。您可以在 then 回调中绘制图表。

我用这个 fiddle 做了实验,使用了一些假数据。

关于javascript - 在绘制图表之前在每个循环中编排嵌套的 AJAX 调用 (JQuery),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27144249/

相关文章:

php - 上传文件后将文件名保存在数据库中

ajax - 如何在cakephp中检查Ajax请求?

JQuery 变量始终具有相同的值

javascript - 在javascript上获取数组值空间后的字符

javascript - 有没有更有效的方法来查找返回对象的子集?

javascript - 在 PHP 和 Mysql 的 jquery 日期选择器中禁用给定范围内的日期

javascript - JS 对象不工作

javascript - Sencha touch 2 ProgressBar.js 文件未找到

javascript - 你的 JavaScript 工具链中有什么?

javascript - $.ajax json 下拉菜单项