13.3 jqXHR 对象
在发出Ajax请求时,jQuery会帮我们确定取得数据的最佳方式。可用的方式包括标准的XMLHttpRequest
对象、微软的ActiveX对象XMLHTTP
,或者<script>
标签。
由于不同请求使用的数据传输方式可能不一样,那我们就需要一个公共的接口与这些通信交互。为此,jqXHR
对象提供了这种接口:在XMLHttpRequest
对象可用的情况下,封装该对象的行为;在XMLHttpRequest
对象不可用的情况下,则尽可能模拟它。这个对象提供给我们的属性和方法包括:
包含返回数据的
.responseText
或.responseXML
;包含状态码和状态描述的
.status
和.statusText
;操作与请求一起发送的HTTP头部的
.setRequestHeader()
;提早中断通信的
.abort()
。
jQuery的所有Ajax方法都会返回jqXHR
对象,只要把这个对象保存起来,随后就可以方便地使用这些属性和方法。
13.3.1 Ajax承诺
与标准的XMLHttpRequest
对象相比,jqXHR
对象有一点非常值得重视,那就是它也是一个承诺对象。在第11章讨论延迟对象时,我们知道可以通过它来设置在某个操作完成后触发的回调函数。Ajax调用就是这样一种操作,而jqXHR
对象提供了延迟对象所承诺的方法。
使用这些承诺对象的方法,可以重写$.ajax()
调用,把success
和error
回调函数替换成如下所示,参见代码清单13-7。
代码清单13-7
- $.ajax({
- url: 'http://book.learningjquery.com/api/',
- dataType: 'jsonp',
- data: {
- title: $('#title').val()
- },
- timeout: 15000
- })
- .done(response)
- .fail(function() {
- $response.html(failed);
- });
乍一看,调用.done()
和.fail()
与之前的写法相比并没有明显的好处。可是,这两个承诺方法的确是有好处的。第一,可以多次调用这两个方法,根据需要添加多个处理程序。第二,如果把调用$.ajax()
的结果保存在一个变量中,那么就可以考虑代码的可读性,在后面再添加处理程序。第三,如果在添加处理程序的时候Ajax操作已经完成,就会立即调用该处理程序。第四,我们最好采用与jQuery库中其他代码一致的语法,这带来的好处不言而喻。
使用承诺方法的另一个好处是可以在请求期间添加一个加载指示器,然后在请求完成时或在其他情况下隐藏它。这时候,使用.always()
方法就非常方便,参见代码清单13-8。
代码清单13-8
- $ajaxForm.on('submit', function(event) {
- event.preventDefault();
- $response.addClass('loading').empty();
- $.ajax({
- url: 'http://book.learningjquery.com/api/',
- dataType: 'jsonp',
- data: {
- title: $('#title').val()
- },
- timeout: 15000
- })
- .done(response)
- .fail(function() {
- $response.html(failed);
- })
- .always(function() {
- $response.removeClass('loading');
- });
- });
在发送$.ajax()
调用之前,给#response
容器添加loading
类。而在加载完成时,则删除这个类。这样,就可以进一步增强用户的体验。
接下来,我们把$.ajax()
调用的结果保存在变量里,以备将来使用。从中你可以真正理解这种承诺行为带给我们的好处。
13.3.2 缓存响应
如果想重复使用同一段数据,那么重复发送Ajax请求显示是一种浪费。为了避免这样做,可以把返回的数据缓存在一个变量中。在需要使用某些数据时,可以检查缓存中是否有这些数据。如果有,就直接拿来用即可。如果没有,则需要发送Ajax请求,并在它的.done()
处理程序中将数据保存在缓存里,然后再操作返回的数据。
说起来简单,做起来得需要好几步。不过,要是能够利用承诺对象的属性,那就非常简单了,如代码清单13-9所示。
代码清单13-9
- var api = {};
- $ajaxForm.on('submit', function(event) {
- event.preventDefault();
- $response.empty();
- var search = $('#title').val();
- if (search == '') {
- return;
- }
- $response.addClass('loading');
- if (!api[search]) {
- api[search] = $.ajax({
- url: 'http://book.learningjquery.com/api/',
- dataType: 'jsonp',
- data: {
- title: search
- },
- timeout: 15000
- });
- }
- api[search].done(response).fail(function() {
- $response.html(failed);
- }).always(function() {
- $response.removeClass('loading');
- });
- });
这里,我们新声明了一个变量,名叫api
,用来保存创建的jqXHR
对象。这个变量本身是一个对象,它的键对应着执行的搜索关键词。在提交表单时,我们要检查一下jqXHR
对象中是否有那个键。如果没有,就像以前一样执行查询,并把结果对象保存在api
变量中。
有了jqXHR
对象,也就有了.done()
、.fail()
和.always()
处理程序。注意,无论是否发送Ajax请求,jqXHR
对象都会存在。现在这里需要考虑两种可能性。
首先,如果以前没有查询过,那么可能会发送Ajax请求。这样的结果跟以前一样:发送请求,然后使用承诺方法给jqXHR
对象添加处理程序。当服务器返回响应时,触发适当的回调函数,将结果输出到屏幕上。
另一方面,如果以前执行过这个查询,那么jqXHR
对象已经保存在api
里面了。这一次不会执行新的查询,但我们仍然可以在保存的对象上调用承诺方法。而这会给该对象添加新的处理程序,由于作为延迟对象它已经被解决了,所以会立即触发相关的处理程序。
jQuery的延迟对象系统会在后台把所以这些工作都替我们做了。我们只要写几行代码,就可以消除应用中不必要的网络请求。