当前项目对用户体验的要求层出不穷,本篇通过所历项目与前辈网文日志加以记录总结。
多个 ajax 请求的各类解决方案:同步,队列,cancel 请求,本章末尾提供 demo,或点这里。
注意:能一个 ajax 请求解决的问题绝不用两个 ajax 请求来处理,没有最好的方案,只有最合适的方案。
需求大致分三类,13,24和5:
1.多个 ajax 请求同时发送,相互无依赖。
2.多个 ajax 请求互相依赖,必须有先后顺序。
3.多个 ajax 请求被同时发送,只需要最后一个请求。
4.多个 ajax 请求返回的是同一个数据的不同部分,我们需要在前端把这些数据组合成一个完整的数据来运用,即需要知道数据的次序来正确拼接。
5.不同查询条件下的同一组数据,我们需要最新的一组数据。
第 2 种 case
应用场景:多个 ajax 请求,需要顺序执行,后一个 ajax 请求的执行参数是前一个 ajax 的结果。
一个非常经典的例子:用户登录后我们发送一次请求得到用户的应用 ID,然后利用应用 ID 发送一次请求得到具体的应用内容。
处理方法:
1.利用 ajax 参数 async 设置为 false,进行同步操作(这个方法只适合同域操作,浏览器可能失去响应,体验糟糕,跨域需使用下面两种方法)
2.利用 ajax 嵌套
3.利用队列进行操作,jquery ajax 队列操作核心代码:
(function ($) { var ajaxRequest = {}; $.ajaxQueue = function (settings) { var options = $.extend({ className: 'DEFEARTNAME' }, $.ajaxSettings, settings); var _complete = options.complete; $.extend(options, { complete: function () { if (_complete) _complete.apply(this, arguments); if ($(document).queue(options.className).length > 0) { $(document).dequeue(options.className); } else { ajaxRequest[options.className] = false; } } }); $(document).queue(options.className, function () { $.ajax(options); }); if ($(document).queue(options.className).length == 1 && !ajaxRequest[options.className]) { ajaxRequest[options.className] = true; $(document).dequeue(options.className); } }; })(jQuery);
第 3 种 case
应用场景:比较典型的是 autocomplete 控件的操作,case 2 解决方案有些浪费。
解决方法:保留最后一次请求,cancel之前的请求,代码如下:
(function ($) { var jqXhr = {}; $.ajaxSingle = function (settings) { var options = $.extend({ className: 'DEFEARTNAME' }, $.ajaxSettings, settings); if (jqXhr[options.className]) { jqXhr[options.className].abort(); } jqXhr[options.className] = $.ajax(options); }; })(jQuery);
第 4 种 case
如果中间某一个 ajax 没有正常返回,后面的 ajax 有不执行的风险。
解决方法:递归,在回调函数中执行下一个 ajax,但需要做一些特别的处理。可以用闭包来记录当前是第几组的 ajax 请求,回调函数中读取。或前后端协商标识符,但一些公共接口数据不是我们能左右的。
第 5 种 case
应用场景:选择查询条件并隔一段时间实时刷新。如实时显示地图绘制,实时图表绘制等。后面的数据要刷新前面的数据,比如状态的实时更新。
1.更新了查询条件,中断当前所有 ajax 请求。清空 ajax 序列的数组,或定时发送请求。
2.未更新查询条件,隔一段时间自动刷新数据,理论上我们只需要最新的一组数据。
解决方法:设置超时,超时的时间不要超过请求的间隔。这样当发起新的 ajax 请求时,上一个 ajax 已经返回结果或者超时取消了。
若网络条件差,超时的时间或两次 ajax 请求之间的间隔又比较短,可能一组数据都拿不下来,页面发呆。所以可不设置超时,能拿下一组数据是一组,拿到数据后再清除 ajax 序列中此请求前面的 ajax 请求或者简单中断当前所有请求的 ajax。
资料:
1.ajax 中断查询 abort() 方法
2.封装error 和超时功能:封装的JSONP跨域函数 待整理。
3.ajax 序列,即将所有的 ajax 请求放到一个数组中去,方便控制,参考:
var xhr = $.ajax({ url: '__SELF__', type: 'get', dataType: 'json', cache:false, data: {stationid: StationID }, success:function(data){ }); xhrArr.push(xhr);
某500强企业操作平台解决方案,待整理。
由于多个 ajax 请求的响应时间无法控制,可以用 ?解决。完整 demo 如下:
body: <form id="form1" runat="server"> <input type="button" id="btnLaunchAsync" value="Launch Asynchronous Request" /> <input type="button" id="btnLaunchSync" value="Launch Synchronous Request" /> <input type="button" id="btnLaunchQueue" value="Launch Requested Queue" /> <input type="button" id="btnLaunchSingle" value="Launch Single Request" /> <div id="txtContainer"></div> </form> JavaScript: (function ($) { var jqXhr = {}, ajaxRequest = {}; $.ajaxQueue = function (settings) { var options = $.extend({ className: 'DEFEARTNAME' }, $.ajaxSettings, settings); var _complete = options.complete; $.extend(options, { complete: function () { if (_complete) _complete.apply(this, arguments); if ($(document).queue(options.className).length > 0) { $(document).dequeue(options.className); } else { ajaxRequest[options.className] = false; } } }); $(document).queue(options.className, function () { $.ajax(options); }); if ($(document).queue(options.className).length == 1 && !ajaxRequest[options.className]) { ajaxRequest[options.className] = true; $(document).dequeue(options.className); } }; $.ajaxSingle = function (settings) { var options = $.extend({ className: 'DEFEARTNAME' }, $.ajaxSettings, settings); if (jqXhr[options.className]) { jqXhr[options.className].abort(); } jqXhr[options.className] = $.ajax(options); }; })(jQuery); var ajaxSleep = (function () { var _settings = { type: 'GET', cache: false, success: function (msg) { var thtml = $('#txtContainer').html(); $('#txtContainer').html(thtml + "<br />" + msg); } }; return { get: function (seconds, mode, isAsync) { var mode = mode || 'ajax', isAsync = isAsync || false; $[mode]($.extend(_settings, { url: "ResponsePage.aspx?second=" + seconds, async: isAsync, className: 'GET' })); }, post: function (seconds, mode, isAsync) { var mode = mode || 'ajax', isAsync = isAsync || false; $[mode]($.extend(_settings, { type: 'POST', url: "PostPage.aspx", data: { second: seconds }, async: isAsync, className: 'POST' })); } }; } ()); var launch = function (settings) { $('#txtContainer').html(''); var mode = settings.mode, isAsync = settings.isAsync; ajaxSleep.get(12, mode, isAsync); ajaxSleep.get(10, mode, isAsync); ajaxSleep.get(8, mode, isAsync); ajaxSleep.post(6, mode, isAsync); ajaxSleep.post(4, mode, isAsync); ajaxSleep.post(2, mode, isAsync); } $(document).ready(function () { //第1种case $('#btnLaunchAsync').click(function () { launch({ isAsync: true }); }); //第2种case $('#btnLaunchSync').click(function () { launch({}); }); //第2种case $('#btnLaunchQueue').click(function () { launch({ mode: 'ajaxQueue', isAsync: true }); }); //第3种case $('#btnLaunchSingle').click(function () { launch({ mode: 'ajaxSingle', isAsync: true }); }); });