jQuery的Deferred对象,Promise对象

Ajax

Ajax

Ajax是实现异步操作的技术,用于创建快速动态网页的技术。可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

传统的网页(不使用 AJAX)如果需要更新内容,必须重载整个网页页面。

没有Deferred对象的ajax

$.ajax({
     url: 'xxx',
     success: function(){}
     error: function() {}
})

 

如果在要实现多个ajax的操作,那么就要在success: function(){}里面再次嵌套$.ajax({});方法。不可以链式操作。如果多层回调的话,那么代码不美观,可读性也差。

 

基于这样的背景,jQuery1.5之后引入了Deferred对象,Deferred对象就是jQuery的回调函数的解决方案。

Deferred对象和promise对象都是用来管理异步操作,是对ajax过程的一种抽象。从程序的角度来减少回调嵌套。

Deferred对象

jQuery1.5引入Deferred对象之后,可以实现链式操作写法。

$.ajax('/xxx').success(function(){}).fail(function(){})

 

它定义了两种基本状态:resolved和 rejected,然后定义了三个方法success,fail,always来定义当deferred对象从初始状态转换到两种基本状态时候应该调用哪 些函数。最后提供了reject/rejectWith,resolve/resolveWith来改变deferred对象的状态,以便出发对应的回调函数。

 

如果ajax操作成功后,除了原来的回调函数,我还想再运行一个回调函数,直接把它加在后面就行了。

  $.ajax("test.html")

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

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

  .done(function(){ alert("第二个回调函数!");} );

 

$.when()

对Promise对象也适用。

如果想为多个操作添加一个回调函数,Deferred对象也提供了相应的方法,也就是$.when()方法。

$.when($.ajax("test1.html"), $.ajax("test2.html"))

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

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

    这段代码的意思是,先执行两个操作$.ajax("test1.html")和$.ajax("test2.html"),如果成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。

 

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

比如,

 var wait = function(){

    var tasks = function(){

      alert("执行完毕!");

    };

    setTimeout(tasks,5000);

  };

$.when(wait())

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

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

但是,when的参数只能是deferred对象,对wait进行改写。

var dtd = $.Deferred(); // 新建一个deferred对象

  var wait = function(dtd){

    var tasks = function(){

      alert("执行完毕!");

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

    };

    setTimeout(tasks,5000);

    return dtd.promise();

  };

这里有两个地方需要注意。首先,最后一行不能直接返回dtd,必须返回dtd.promise()。 原因是jQuery规定,任意一个deferred对象有三种执行状态----未完成,已完成和已失败。如果直接返回dtd,$.when()的默认执行 状态为"已完成",立即触发后面的done()方法,这就失去回调函数的作用了。dtd.promise()的目的,就是保证目前的执行状态----也就 是"未完成"----不变,从而确保只有操作完成后,才会触发回调函数。

    其次,当操作完成后,必须手动改变Deferred对象的执行状态,否则回调函数无法触发。dtd.resolve()的作用,就是将dtd的执行状态从"未完成"变成"已完成",从而触发done()方法。

    最后别忘了,修改完wait之后,调用的时候就必须直接传入dtd参数。

  $.when(wait(dtd))

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

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

$.then()

对Promise对象也适用。

用于给一个deferred对象添加监听器。该方法接受两个参数:

deferred.then( doneCallbacks, failCallbacks )

doneCallbacks: 一个函数,或者是一组函数,在deferred被实现时调用
failCallbacks: 一个函数,或者是一组函数,在deferred被拒绝时调用

例子:

Js代码  

  1. $.get("test.php").then(  
  2.     function(){ alert("$.get succeeded"); },  
  3.     function(){ alert("$.get failed!"); }  
  4. );  



deferred.then()提供了一种方便的写法,让一个deferred在实现或者被拒绝时的监听函数可以写在一个api中。

Promise对象

Promise对象其实就是deferred对象的特例,因为Promise对象不能更改异步状态,而deferred对象可以。

promise方法已经被混入deferred对象了,但是deferred对象的一些方法没有在promise中,主要就是触发状态转换和函数回调的几个方法比如reject/rejectWith。
所以promise能做的 deferred都能做到,但是deferred能触发状态转换确实promise做不到的。所以使用promise方法返回promise对象可以防止 被其他人改变自己的状态,他们只能通过promise来注册回调,但是不能通过promise来改变状态。

promise对象常见的方法有三个:done,fail,then。

其它的方法就不要去记了,jQuery这里的接口方法太多了,在我看来挺啰嗦的,就跟早期的事件方法绑定一样,live,delegate,bind,最终不是都归为on来管了么。

代码示例,如下:

1.DOM使用Promise方法:

var box=$('#box');
box.promise().done(function(ele){
    console.log(ele);//jQuery box
});

2.Ajax使用Promise方法(默认返回一个Promise对象,所以可以不必显式调用Promise方法):

$.post('/',{}).done(function(data){
    console.log('请求成功');
}).fail(function(){
    console.log('请求错误');
});

 

Deferred对象和Promise对象

promise对象常见的方法有三个:done,fail,then。

对于deferred对象呢,也就是使用$.Deferred()方法,以及$.when()等方法创造出来的对象,有如下的常用方法:

  • resolve,reject,notify;
  • done,fail,progress;

另外还有promisethenalways方法。

之所以这么排版,是因为他们是对应的,也就是说:resolve方法会触发done的回调执行,reject会触发fail的回调,notify会触发progress的回调。

 

直接看代码:

var wait = function(ms) {
    var dtd = $.Deferred();
    setTimeout(dtd.resolve, ms);
    // setTimeout(dtd.reject, ms);
    // setTimeout(dtd.notify, ms);
    return dtd.promise(); //此处也可以直接返回dtd
};
 
wait(2500).done(function() {
    console.log('haha,师太,你可让老衲久等了');
}).fail(function() {
    console.log('失败了');
}).progress(function(res) {
    console.log('等待中...');
});

我们看到了,上面的代码中,在wait函数中,返回的是个Promise对象,而不是deferred对象。

要知道,Promise对象是没有resolve,reject,notify等方法的,也就意味着,你无法针对Promise对象进行状态更改,只能在done或fail中进行回调配置。所以,你如果这么调用wait(2500).resolve()将会报错,因为wait(2500)返回的是个Promise对象,不存在resolve方法。

但是,这么做,有个好处,我们把dtd这个deferred对象放在了wait函数中,作为了局部变量,避免了全局的污染;进一步通过Promise方法,转化dtd这个deferred对象为Promise对象,避免了函数wait外部可能发生的状态更改(假如我们确实有这个需求)。

posted on 2015-06-23 22:22  alicewang999  阅读(281)  评论(0编辑  收藏  举报