Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

jQuery的Deferred对象

  jQuery的方法链应该都是耳熟能详的东西了,从jQuery最早的版本开始,jQuery对象基本都是基于方法链使用的。但是在早期并没有明显的异步方法链这个概念,异步方法的回调都是作为参数传入的。直到jQuery 1.5才引入Deferred对象来解决了这个异步问题。
  昨天的文章中就有涉及到这个概念,先来看看昨天的例子
$.post("test.php").done(function(){
  //成功时的操作
});
  done的参数是这个post请求成功后的回调函数。post本身是异步的,而done却是在这个异步方法之后以方法链的方式调用的。这整个就是一个异步方法链。而我们在jQuery源代码的ajax(post是ajax的派生方法)这个函数中可以找到它的返回值是一个Deferred对象(development jQuery 1.9.1 第7753行)。也就是说,post这个方法实现这样的异步方法链就是使用了Deferred这个对象。而jQuery本身也提供这个Deferred对象,有详细文档(在文章末尾有链接)。我们了解了这个Deferred对象,就可以在jQuery的一些异步方法中使用更复杂的异步方法链操作,也可以在自己定义的函数中使用这个对象以加入异步方法链的功能。
  既然jQuery有提供详细文档并且有例子了,我就不说太多了,简单的把这个对象的各个方法整理一遍吧。其实,这个对象的方法分为两类。一类是内部调用的,就比如上面这个例子在post这个方法的内部调用Deferred对象上的方法,就勉强叫做“私有方法”吧。另一种就是类似上面的done,是链式调用时使用的,咱就称为“公共方法”了(叫法有点牵强,不喜勿喷,理解就好)。咱先来看看私有方法。
  deferred.resolve(args)
  deferred.reject(args)
  这两个方法是最关键的两个私有方法,调用resolve时表示异步处理成功,调用reject时表示异步处理失败。它们调用时就相当于触发了在方法链中绑定的相关回调函数。比如resolve调用时候,就会触发方法链条上用done绑定的回调函数,因为resolve是表示处理成功,而done绑定的回调函数表示处理成功时的动作。reject也有类似的相关方法叫做fail,这个我们后面再说了。另外,这个两个函数的参数就是传递给回调函数的参数。
  deferred.resolveWith(context,[args])
  deferred.rejectWith(context,[args])
  这两个方法其实就是上面两个方法的派生方法,上面两个方法是直接调用回调函数并传入参数,而这两个方法则是使用apply的模式调用回调函数。也就是说,context这个参数会被作为回调函数中的this来使用,而args必须是一个数组。
  deferred.notify(args)
  deferred.notifyWith(context,[args])
  这两个方法的关系和上面两组方法的关系一样。它们对应的绑定回调函数的方法是progress。之所以不把这个也放入上面两组中是因为这个函数的含义是在异步处理过程中回调,区别于上面两个方法是异步处理完成时回调。也就是说,在一次异步处理中可以调用多次notify,但是只能调用一次resolve或reject。其实这个方法的语言含义是发出一个通知,这很容易理解吧。另外这个方法是jQuery 1.7才加入的,之前的那些在1.5版本就有了。
  deferred.promise([target])
  最后还有一个很重要的promise方法。我们之所以要区分开私有方法和公共方法就是因为它。这个方法的功能是创建一个原deferred的副本,这个副本中的方法和原对象的方法是映射关系,但是这个副本只包含公共方法。这个方法的返回值就是它创建的副本对象。这个方法的存在是为了防止私有方法在外部被调用。它的参数是一个对象,这个对象上的属性会被复制一份到他创建的副本对象上。这个可以避免我们自己手动去给返回值添加属性的麻烦。
  以上这些就是Deferred的私有方法了,下面来看看公共方法。
  deferred.done(callbacks)
  deferred.fail(callbacks)
  deferred.progress(callbacks)
  这三个方法在前面的私有方法中已经涉及到了,分别对应什么私有方法我就不重复说了。它们的功能是绑定各种操作的回调函数,参数可以是多个,也可以是一个,但是都是函数或函数的数组就对了。另外,它们可以调用多次,绑定的函数也会叠加上。
  deferred.always(callback[,callback])
  这个方法也是绑定回调函数,但是它并不是绑定到某个操作上。当Deferred对象的异步处理完成时候就会触发它,注意这里是完成而不是成功或失败。无论成功还是失败都会完成,也就是说无论内部是调用resolve还是reject它都会被触发(调用notify不会触发,因为它没完成)。这个方法是jQuery 1.6加入的。
  deferred.then(callback[,callback[,callback]])
  这个then方法在jQuery中的修改次数比较多,很多版本的参数都不同。1.6时它有两个参数,而1.8时候可以有3个参数。不过这些参数都是函数,其实这个then方法就是上面done、fail、progress,这三个方法的快捷绑定方式。调用then传入三个参数就会分别被当作是done、fail、progress,这三个方法的参数使用而把参数绑定到相应的动作上。注意jQuery的版本和参数的顺序就没什么问题了。
  deferred.pipe(callback[,callback[,callback]])
  这个方法的功能是把绑定的动作回调函数分组处理,这是我自己对它的理解,官方文档描述的更奇葩,我也看的一头雾水。它的三个参数和then类似,也是对应到三个动作的。但只是这个三个参数不是动作的回调函数,而是对动作回调函数参数的预处理函数。感觉说的有点绕了,其实很简单。这个我还是写个例子吧。这里就拿done和resolve举例
//创建一个Deferred对象
var def=new $.Deferred;
//直接调用成功,并传入参数
def.resolve(3);

//第一组回调
def.pipe().done(function(e){
  console.log("第一组第一个:"+e);
}).done(function(e){
  console.log("第一组第二个:"+e);
});

//第二组回调
def.pipe(function(e){
  return e*3;
}).done(function(e){
  console.log("第二组第一个:"+e);
}).done(function(e){
  console.log("第二组第二个:"+e);
});
  这个例子就是把回调函数分成两组,第一组pipe没有参数,所以直接把resolve的参数穿给done绑定的函数;第二组由于pipe的第一个参数是存在的,所以resolve的参数先放入pipe的回调中处理,让后才把返回值传给done绑定的回调函数。另外,如果是resolveWith触发的,this在done绑定的回调函数中也依然有效。
  deferred.isRejected()
  deferred.isResolved()
  这个两个方法简单,从字面上就能理解。返回一个布尔值判断当前deferred对象是否已经成功或失败。
  deferred.state()
  这个方法也很简单,获取当前deferred对象的状态,返回的是字符串,共有三种情况。pending:未完成,resolved:已成功,rejected:已失败。
  以上这些就是公共方法了。除了私有方法和公共方法外,还有个特殊的是直接在jQuery根对象上和Deferred相关的方法,就是下面这个when方法
  jQuery.when(deferreds)
  这个东西比较特殊吧,它的参数是一个或多个Deferred对象,返回一个新的Deferred对象。它的功能是把参数中这些Deferred对象合并。怎么合并呢?这个有点麻烦,由于都是Deferred对象,下面简称为对象。只有所有对象都完成是才会触发完成,而任意一个对象发出通知时就会触发通知。完成以后呢?只有所有对象都成功是才会触发成功,而只要有任意一个对象失败时候就会触发失败。
  呼,终于完啦,整理这个累死我了。我的说明基本不带实例的,因为很多实例官方文档中都有,我就懒的写了。如果有什么不明白的地方或我说错的地方就跟帖吧。
  另外,这篇文章我整理了一晚上,要是有转载的务必留下原文地址呀。
参考:
  http://api.jquery.com/category/deferred-object/
网名:
34.203.213.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^