假如现在有三个选项卡,点击某个选项卡就通过ajax的方式加载出其中的内容,如果网速过慢导致内容没有加载出来,用户就点击其他的选项卡,如何保证加载出来的结果是正确的,和能否将请求延迟发送等?
这是个好问题。不知道题主是否熟悉自由测试和弱网测试这两个提法——这其实就是你提出的测试需求。
简而言之:
自由测试就是乱点;
弱网测试就是人为制造掉包和延迟(可能制造成随机或确定性的)。
这两个测试都是非常重要的。程序能否顶住这两项测试,保证一切情况下的响应都是合理的(而不是跑飞),这是开发者对健壮性把握如何的一个重要指标。
问题一分为二地看。先忽略“点击速率的控制”,仅看“如何保证加载结果正确”这一点。
从体验的角度来看,用户点击多个选项卡时,内容应该仅以用户点击的最后一个选项卡为准。毕竟用户点击了新的选项卡,就包含着“之前没加载出来的旧选项卡,全都丢弃不要了”的潜台词。
考虑这个序列:选项卡1点击 - 选项卡2点击 - 选项卡1响应到达 - 选项卡2响应永远丢包。此时用户体验来看,弹出选项卡1必然是怪异的(我明明点击的是选项卡2!)。
选项卡1点击 - 选项卡2点击 - 选项卡1响应到达 - 选项卡2响应永远丢包
唯一的正确答案是:显示为选项卡2永远加载中(或者提示超时出错,允许用户重新加载),而永远丢弃选项卡1的响应。
从这个意义上,AJAX请求的发出你是不应当阻止的。你的真正需求是:发出新的AJAX请求的时候,如何将旧的请求全部停下来。
这里必须说明的是:AJAX对象,保证HTTP的响应与请求一一对应。
具体而言: 某个具体的XMLHttpRequest实例发出了HTTP请求。 那么此HTTP请求的响应,就会回到发请求的那个XMLHttpRequest实例上。 这一点自动、必然、绝对、100%准确无误,并且由浏览器(或JS引擎)直接保证,无需任何编程干预。 以上是针对原生AJAX而言的。jQuery也一样,只是对象变成了jQuery封装过的jqXHR而已。 1次AJAX请求必然有1个实例,多个请求那就有多个实例来管理,这与任何其他条件无关,根本不用考虑“多个AJAX请求相同页面,响应会不会对应乱了”这种杞人忧天的问题。 你说回调?那只不过是挂载在各个AJAX对象实例上的一个普通成员变量(JavaScript里函数和变量同为一等公民)。请求对象对应正确了,回调自然也不会乱。
具体而言:
某个具体的XMLHttpRequest实例发出了HTTP请求。
XMLHttpRequest
那么此HTTP请求的响应,就会回到发请求的那个XMLHttpRequest实例上。
这一点自动、必然、绝对、100%准确无误,并且由浏览器(或JS引擎)直接保证,无需任何编程干预。
以上是针对原生AJAX而言的。jQuery也一样,只是对象变成了jQuery封装过的jqXHR而已。
jqXHR
1次AJAX请求必然有1个实例,多个请求那就有多个实例来管理,这与任何其他条件无关,根本不用考虑“多个AJAX请求相同页面,响应会不会对应乱了”这种杞人忧天的问题。
你说回调?那只不过是挂载在各个AJAX对象实例上的一个普通成员变量(JavaScript里函数和变量同为一等公民)。请求对象对应正确了,回调自然也不会乱。
这种1次请求对应1个对象的关系,就给了我们在AJAX请求发出后,仍然能对其进行控制的可能。
我们确实通常把 $.ajax() 当语句使用($.ajax(settings);),但事实上 $.ajax() 是有返回值的。$.ajax() 返回此次请求对应的 jqXHR 对象,我们可以通过此对象,来影响和操作这次请求本身。
$.ajax()
$.ajax(settings);
那么每次点击选项卡都发出请求,但只响应用户发出的最后一个请求的代码就非常好写了:
$(function() { $('#单选你的选项卡的容器').data('request_buffer', null); }); $('.多选你的每一个选项卡').click(function() { // 旧的HTTP请求直接放弃加载 var previous_jqxhr = $('#单选你的选项卡的容器').data('request_buffer'); if (previous_jqxhr) { previous_jqxhr.abort(); } var current_jqxhr = $.ajax({ type: your_type, url: your_url, data: your_data, timeout: your_timeout_seconds * 1000, context: this, }) .beforeSend(function() { // 显示点loading小动画什么的 }) .done(function() { // 点亮你点击的选项卡,灭掉其他的 $('.多选你的每一个选项卡').removeClass('.选项卡点亮的效果'); $(this).addClass('.选项卡点亮的效果'); // 填充正文区域 $('#单选你显示正文的区域').html(你得到的响应正文); }) .fail(function() { // 你认为合适的超时处理 }); // 新的请求顶掉旧的请求 $('#单选你的选项卡的容器').data('request_buffer', current_jqxhr); });
回调函数是控制HTTP请求的jqXHR对象调用的,所以如果不加污染,那么回调函数内的this指的是jqXHR本身。那么回调函数在调用到的时候,根本没有办法反查到你点击了哪个选项卡。
this
所以一定注意代码里那个context。jqXHR对象的context,确定了jqXHR在调用回调函数的时候,把回调函数内看到的this污染成谁。
context
只有在产生jqXHR的时候(即调用$.ajax()时)明确告知“此请求和哪些对象有联系”,在回调的时候才不会迷失方向,导致一些设置视觉效果的需求做不出来。
实现要基于事物的本源。如果一个AJAX请求要丢弃,那就应当把请求对象本身挖出来,通知他自己放弃。
这样不但彻底把待丢弃的无效回调本身消灭,更可以命令浏览器直接断开HTTP连接,节省宝贵的流量和并发数。这一点也是很重要的。
而明知道请求用不着了还要接收下来,再以“提前return”之类的修补手段“手工丢弃”,这个绕圈子的方案明显是不够优的。
实际上以上的措施,已经能够达到“保证加载结果正确”的目的了。 用户点得快,发出的请求多又怎么样? 反正同一时刻同时只有1条请求在网上跑,只有1个回调有调到的可能,一切的干扰要素都排除光了。
在此基础上,如果引入“限制用户点击的速率”,那么就是纯粹为了减轻服务器压力考虑了。
这个的办法就更加简单:用户点击一个选项卡(启动HTTP请求发送,可以挂在beforeSend事件上)时把所有的选项卡置灰。(不能点是必须明确提示用户的)
beforeSend
然后等待以下两个触发条件触发任意一个,就可以把所有的选项卡恢复点击:
成功分支:用户点的这个选项卡加载成功了(立刻允许用户切换到其他选项卡)
失败情况:用户点击之后过去了 X 秒(加载不出来了,允许用户发出新的请求)
这个的代码就略了。
两个方案:
ajax期间,其他tab全部disabled
设个变量loading,记录是否在请求,只有当loading为false时才发请求
1,首先需要确定的是用户需要连续发送两次请求 无论结果如何两次请求不干扰都需要有效,那我们是不应该阻止用户请求的。
2,如果第二次的请求需要第一次的请求结束后才能发起,那我们肯定是应该要阻止的。
3,至于楼主说的恶意点击,你是说不想服务器多次处理这样的请求么?那是服务器的事情了。
在进行与界面更新相关的AJAX操作时,应考虑在请求前对页面进行遮罩,请求完成后解除,以此来提升界面的响应性以及规避用户再次请求。
取消其他请求或者定时器控制请求都ok。不过我觉得也可以不用考虑取消。都加载回来然后缓存到本地,保证同一数据请求一次。当然也可以结合考虑
我只想说一点,ajax可以设置成同步请求,如果我做这个需求,我会在用户点击一个选项卡之后启动一个遮罩loading层,锁住UI,使用户无法点击其它的操作,然后超时、响应什么的你再具体处理
这是个好问题。不知道题主是否熟悉自由测试和弱网测试这两个提法——这其实就是你提出的测试需求。
简而言之:
自由测试就是乱点;
弱网测试就是人为制造掉包和延迟(可能制造成随机或确定性的)。
这两个测试都是非常重要的。程序能否顶住这两项测试,保证一切情况下的响应都是合理的(而不是跑飞),这是开发者对健壮性把握如何的一个重要指标。
问题一分为二地看。先忽略“点击速率的控制”,仅看“如何保证加载结果正确”这一点。
从体验的角度来看,用户点击多个选项卡时,内容应该仅以用户点击的最后一个选项卡为准。毕竟用户点击了新的选项卡,就包含着“之前没加载出来的旧选项卡,全都丢弃不要了”的潜台词。
考虑这个序列:
选项卡1点击 - 选项卡2点击 - 选项卡1响应到达 - 选项卡2响应永远丢包
。此时用户体验来看,弹出选项卡1必然是怪异的(我明明点击的是选项卡2!)。唯一的正确答案是:显示为选项卡2永远加载中(或者提示超时出错,允许用户重新加载),而永远丢弃选项卡1的响应。
从这个意义上,AJAX请求的发出你是不应当阻止的。你的真正需求是:发出新的AJAX请求的时候,如何将旧的请求全部停下来。
这里必须说明的是:AJAX对象,保证HTTP的响应与请求一一对应。
这种1次请求对应1个对象的关系,就给了我们在AJAX请求发出后,仍然能对其进行控制的可能。
我们确实通常把
$.ajax()
当语句使用($.ajax(settings);
),但事实上$.ajax()
是有返回值的。$.ajax()
返回此次请求对应的jqXHR
对象,我们可以通过此对象,来影响和操作这次请求本身。那么每次点击选项卡都发出请求,但只响应用户发出的最后一个请求的代码就非常好写了:
回调函数是控制HTTP请求的
jqXHR
对象调用的,所以如果不加污染,那么回调函数内的this
指的是jqXHR
本身。那么回调函数在调用到的时候,根本没有办法反查到你点击了哪个选项卡。所以一定注意代码里那个
context
。jqXHR
对象的context
,确定了jqXHR
在调用回调函数的时候,把回调函数内看到的this
污染成谁。只有在产生
jqXHR
的时候(即调用$.ajax()
时)明确告知“此请求和哪些对象有联系”,在回调的时候才不会迷失方向,导致一些设置视觉效果的需求做不出来。实现要基于事物的本源。如果一个AJAX请求要丢弃,那就应当把请求对象本身挖出来,通知他自己放弃。
这样不但彻底把待丢弃的无效回调本身消灭,更可以命令浏览器直接断开HTTP连接,节省宝贵的流量和并发数。这一点也是很重要的。
而明知道请求用不着了还要接收下来,再以“提前return”之类的修补手段“手工丢弃”,这个绕圈子的方案明显是不够优的。
实际上以上的措施,已经能够达到“保证加载结果正确”的目的了。
用户点得快,发出的请求多又怎么样?
反正同一时刻同时只有1条请求在网上跑,只有1个回调有调到的可能,一切的干扰要素都排除光了。
在此基础上,如果引入“限制用户点击的速率”,那么就是纯粹为了减轻服务器压力考虑了。
这个的办法就更加简单:用户点击一个选项卡(启动HTTP请求发送,可以挂在
beforeSend
事件上)时把所有的选项卡置灰。(不能点是必须明确提示用户的)然后等待以下两个触发条件触发任意一个,就可以把所有的选项卡恢复点击:
成功分支:用户点的这个选项卡加载成功了(立刻允许用户切换到其他选项卡)
失败情况:用户点击之后过去了 X 秒(加载不出来了,允许用户发出新的请求)
这个的代码就略了。
两个方案:
ajax期间,其他tab全部disabled
设个变量loading,记录是否在请求,只有当loading为false时才发请求
1,首先需要确定的是用户需要连续发送两次请求 无论结果如何两次请求不干扰都需要有效,那我们是不应该阻止用户请求的。
2,如果第二次的请求需要第一次的请求结束后才能发起,那我们肯定是应该要阻止的。
3,至于楼主说的恶意点击,你是说不想服务器多次处理这样的请求么?那是服务器的事情了。
在进行与界面更新相关的AJAX操作时,应考虑在请求前对页面进行遮罩,请求完成后解除,以此来提升界面的响应性以及规避用户再次请求。
取消其他请求或者定时器控制请求都ok。
不过我觉得也可以不用考虑取消。
都加载回来然后缓存到本地,保证同一数据请求一次。
当然也可以结合考虑
我只想说一点,ajax可以设置成同步请求,如果我做这个需求,我会在用户点击一个选项卡之后启动一个遮罩loading层,锁住UI,使用户无法点击其它的操作,然后超时、响应什么的你再具体处理