jQuery的deferred对象详解

今天介绍jQuery1.5.0版本引入的一个新功能--Deferred.
一,什么是Deferred对象?
开发网站中经常有一些耗时操作,这些耗时操作既有异步操作(ajax读取服务器数据),也有同步操作(遍历一些大型数组),通常这些操作不能马上得到结果,我们这时候就会用回调函数(callback),原来jQuery对回调函数方面的功能比较弱,于是有了Deferred。所以说Deferred是jQuery的异步回调解决方案。
二,ajax链式写法
jQuery的ajax操作的传统写法:

$.ajax({
  url: "test.php",
  success: function(){
    alert("哈哈,成功了!");
  },
  error:function(){
    alert("出错啦!");
  }
});

 
在jQuery1.5.0以下版本$.ajax()返回的xhr对象,没有链式写法,jQuery1.5.0以上版本支持链式写法。

 $.ajax('test.php').done(function() {
     alert('成功');
 }).fail(function() {
     alert('失败');
 });

 
可以看到,done()相当于success方法,fail()相当于error方法。采用链式写法以后,代码的可读性大大提高。

三、为同一操作指定多个回调函数

很简单,还是以上面的代码为例,直接把它加在后面就行了。

 $.ajax('test.php').done(function() {
     alert('成功');
 }).fail(function() {
     alert('失败');
 }).done(function(){
     alert('成功2');
 });
 
 
四、为多个操作指定回调函数

deferred对象的另一大好处,就是它允许你为多个事件指定一个回调函数。
$.when($.ajax('test1.php'),$.ajax('test2.php')).done(function() {
   alert('成功');
}).fail(function() {
   alert('失败');
});
 
这段代码的意思是,如果$.ajax('test1.php')和$.ajax('test2.php')都成功了,就执行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。 五、普通操作的回调函数接口(上)

deferred对象的最大优点,就是它把这一套回调函数接口,从ajax操作扩展到了所有操作。也就是说,任何一个操作----不管是ajax操作还是本地操作,也不管是异步操作还是同步操作----都可以使用deferred对象的各种方法,指定回调函数。

我们来看一个具体的例子。假定有一个很耗时的操作wait:
  var wait = function() {
        var tasks = function() {
            alert('执行完毕');
        }
        setTimeout(tasks, 3000);
  }
  
如何为它指定回调函数?

可以使用$.when():
$.when(wait()).done(function(){
alert('成功');
}).fail(function(){
alert('失败');
});
   
但是,这样写的话,done()方法会先立即执行,然后等待3s执行tasks函数,这并不是我们要的结果,会有这样结果的原因在于$.when()的参数只能是deferred对象,所以我们要对wait()进行改写:
    var dtd = $.Deferred();//创建一个Deferred对象
    var wait = function(dtd) {
        var tasks = function() {
            alert('执行完毕');
            dtd.resolve();//改变Deferred对象执行状态
        }
        setTimeout(tasks, 3000);
        return dtd;
    }
    $.when(wait(dtd)).done(function() {
        alert('成功');
    }).fail(function() {
        alert('失败');
    });
 
wait()函数运行完,就会自动运行done()方法指定的回调函数。

详解:deferred.resolve()方法和deferred.reject()方法
要理解这两个方法先了解deferred对象的三种执行状态: 1,pending:deferred执行状态未完成 2,resolved:deferred执行状态完成 3,rejected:deferred执行状态失败 执行deferred.resolve()方法,deferred执行状态从未完成变成已完成状态(即:resolved状态),done回调函数执行,执行deferred.reject()方法,deferred执行状态从未完成变成失败状态(即:rejected状态),fail回调函数执行。   六、deferred.promise()方法
上面这种写法,由于dtd是一个全局对象,所以它的执行状态可以从外部改变。

请看下面的代码:
 var dtd = $.Deferred(); // 新建一个Deferred对象

  var wait = function(dtd){

    var tasks = function(){

      alert("执行完毕!");

      dtd.resolve(); // 改变Deferred对象的执行状态

    };

    setTimeout(tasks,5000);

    return dtd;

  };

  $.when(wait(dtd))

  .done(function(){ alert("哈哈,成功了!"); })

  .fail(function(){ alert("出错啦!"); });

  dtd.resolve();
 

在代码的尾部加了一行dtd.resolve(),这就改变了dtd对象的执行状态,因此导致done()方法立刻执行,跳出"哈哈,成功了!"的提示框,等5秒之后再跳出"执行完毕!"的提示框。

为了避免这种情况,jQuery提供了deferred.promise()方法。由deferred.promise()方法生成的deferred对象不能改变deferred的执行状态(即不支持deferred.resolve()方法和deferred.reject()方法),但是仍然可以执行done和fail回调函数. 示例:
     var wait = function(dtd) {    
        var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象
        var tasks = function() {      
            alert("执行完毕!");      
            dtd.resolve(); // 改变Deferred对象的执行状态

                
        };

        setTimeout(tasks, 5000);
        return dtd.promise(); // 返回promise对象
    };
      
    $.when(wait()).done(function() {
        alert("哈哈,成功了!");
    }).fail(function() {
        alert("出错啦!");
    });
 
 
七、$.Deferred

另一种防止执行状态被外部改变的方法,是使用deferred对象的构建函数$.Deferred()。

这时,wait函数还是保持不变,我们直接把它传入$.Deferred():
    var awit = function(dtd) {
        var tasks = function() {
            alert('执行完毕');
            dtd.resolve();
        }
        setTimeout(tasks, 3000);
        return dtd.promise();
    }
    $.Deferred(awit).done(function() {
        alert('成功');
    }).fail(function() {
        alert('失败');
    });


jQuery规定,$.Deferred()可以接受一个函数名(注意,是函数名)作为参数,$.Deferred()所生成的deferred对象将作为这个函数的默认参数。

八、在wait对象上部署deferred接口。
var dtd = $.Deferred();
    var awit = function(dtd) {
        var tasks = function() {
            alert('执行完毕');
            dtd.resolve();
        }
        setTimeout(tasks, 3000);
    }
    dtd.promise(awit);
    awit.done(function() {
        alert('成功');
    }).fail(function() {
        alert('失败');
    }).done(function() {
        alert('成功2');
    });
    awit(dtd);
 

这里的关键是dtd.promise(wait)这一行,它的作用就是在wait对象上部署Deferred接口。正是因为有了这一行,后面才能直接在wait上面调用done()和fail()。

十、小结:deferred对象的方法

前面已经讲到了deferred对象的多种方法,下面做一个总结:

  (1) $.Deferred() 生成一个deferred对象。

  (2) deferred.done() 指定操作成功时的回调函数

  (3) deferred.fail() 指定操作失败时的回调函数

  (4) deferred.promise() 没有参数时,返回一个新的deferred对象,该对象的运行状态无法被改变;接受参数时,作用为在参数对象上部署deferred接口。

  (5) deferred.resolve() 手动改变deferred对象的运行状态为"已完成",从而立即触发done()方法。

  (6)deferred.reject() 这个方法与deferred.resolve()正好相反,调用后将deferred对象的运行状态变为"已失败",从而立即触发fail()方法。

  (7) $.when() 为多个操作指定回调函数。

除了这些方法以外,deferred对象还有二个重要方法,上面的教程中没有涉及到。

  (8)deferred.then()

有时为了省事,可以把done()和fail()合在一起写,这就是then()方法。

  $.when($.ajax( "/main.php" ))

  .then(successFunc, failureFunc );

如果then()有两个参数,那么第一个参数是done()方法的回调函数,第二个参数是fail()方法的回调方法。如果then()只有一个参数,那么等同于done()。

  (9)deferred.always()

这个方法也是用来指定回调函数的,它的作用是,不管调用的是deferred.resolve()还是deferred.reject(),最后总是执行。

  $.ajax( "test.html" )

  .always( function() { alert("已执行!");} );

 

 

参考文章:http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html

作者:阮一峰

猜你喜欢

转载自yuyongkun4519.iteye.com/blog/2410089
今日推荐