jQuery的deferred对象详解

本文详细介绍了jQuery中的Deferred对象,解释了其如何解决异步回调问题,并通过实例展示了ajax操作的链式写法、指定同一操作的多个函数、为多个操作指定回调函数等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、什么是deferred对象

开发网站的过程中,我们经常遇到耗时很长的JavaScript操作,其中既有异步操作(比如ajax读取服务器数据),也有同步操作(比如遍历一个大型数组),他们都不是立即能的到结果的。

通常的做法是,为他们指定回调函数,即事先规定,一但他们运行结束,应该调用哪些函数。但是在回调函数方面,jQuery的功能非常弱,为了改变这一点,jQuery开发团队就设计了deferred对象

简单说,deferred对象就是jQuery的回调函数解决方案在英语中,defer的意思是‘延迟’,所以deferred对象的含义就是‘延迟到未来某个点在执行’

二、ajax操作的链式写法
ajax操作的传统写法:

$.ajax({
    url : 'text.html',
    success : function(){
        alert('成功了')
    },
    errer   : function(){
        alert('失败了~')
    }
})

在上面的代码中,$.ajax接收一个对象参数,这个对象包含了两个方法,success方法指定操作成功的回调函数,errer方法指定操作失败的回调方法

$.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法进行链式操作;如果高于1.5.0版本,返回的是deferred对象,可以进行链式操作。
eg :

$.ajax('text.html')
.done(function(){ alert('成功了') })
.fail(function(){ alert('失败了~') })
//done()相当于success方法
//fail()相当于errer方法

三、指定同一操作的多个函数
deferred对象有一大好处,就是他允许你自由添加多个回调函数。
以上面的代码为例,如果ajax操作成功后,除了原来的回调函数,我还想在运行一个回调函数,怎么办?直接加在后面就ok了。

$.ajax('text.html')
.done(function(){ alert('成功了') })
.fail(function(){ alert('失败了~') })
.done(function(){ alert('这是第二个回调函数') })

四、为多个操作指定回调函数
deferred对象的另一个好处,就是可以为多个事件指定一个回调函数,这个传统写法是做不到的。
用到了新的方法 $.when( )

$.when($.ajax('text1.html') , $.ajax('text2.html'))
.done(function(){ alert('成功了') })
.fail(function(){ alert('失败了~') })

这段代码的意思是先执行两个操作,要是都成功了就执行done( )指定的回调函数;如果有一个失败或者都失败了,就执行fail( )指定的回调函数

五、普通操作的回调函数接口
deferred对象的最大优点,就是他把这一套回调函数接口,从ajax扩展到了所有操作,也就是说,任何一个操作,不管是ajax还是本地操作,也不管是异步还是同步操作,都可以使用deferred对象的各种方法,指定回调函数。
eg : 假设有一个很耗时的wait

  var wait  = function (){
            var task = function(){
                alert('task成功')
            }
            setTimeout(task, 5000);
        }

我们为它指定回调函数,应该怎么做,很自然的你会想到,使用$.when( )

$.when(wait())
    .done(function(){ alert("哈哈,成功了!"); })
  .fail(function(){ alert("出错啦!"); });

但是这样写的话,done( )方法会立即执行,起不到回调函数的作用。原因在于$.when( )的参数只能是deferred对象,所以必须对wait( )进行改写

        var dtd = $.Deferred(); //新建一个deferred对象
        var wait  = function (dtd){
            var task = function(){
                alert('task成功')
                dtd.resolve(); //改变deferred对象的执行状态,已完成状态
              //dtd.reject() 失败状态会调用fail()
            }
            setTimeout(task, 5000);
            return dtd
        }

现在wait( )函数返回的是deferred对象了,就可以加上链式操作了

 $.when(wait(dtd))
        .done(function(){ alert('成功') })
        .fail(function(){ alert('失败') })

wait( )函数运行完毕,就可以执行done( )方法执行的回调函数了

六、deferred.resolve( )方法和deferred.reject( )方法
如果仔细看,你会发现在上面的wait()函数中,还有一个地方我没讲解。那就是dtd.resolve()的作用是什么?

要说清楚这个问题,就要引入一个新概念”执行状态”。jQuery规定,deferred对象有三种执行状态—-未完成,已完成和已失败。如果执行状态是”已完成”(resolved),deferred对象立刻调用done()方法指定的回调函数;如果执行状态是”已失败”,调用fail()方法指定的回调函数;如果执行状态是”未完成”,则继续等待,或者调用progress()方法指定的回调函数(jQuery1.7版本添加)。

前面部分的ajax操作时,deferred对象会根据返回结果,自动改变自身的执行状态;但是,在wait()函数中,这个执行状态必须由程序员手动指定。

dtd.resolve()的意思是,将dtd对象的执行状态从”未完成”改为”已完成”,从而触发done()方法。

七、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对象上返回另一个deferred对象,后者只开放与改变状态执行无关的方法(比如done()方法和fail()方法),屏蔽与改变状态有关的方法(比如resolve()和reject()方法),从而使得执行状态不能被改变。

 var dtd = $.Deferred(); // 新建一个Deferred对象
  var wait = function(dtd){
    var tasks = function(){
      alert("执行完毕!");
      dtd.resolve(); // 改变Deferred对象的执行状态
    };

    setTimeout(tasks,5000);
    return dtd.promise(); // 返回promise对象
  };
  var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作
  $.when(d)
  .done(function(){ alert("哈哈,成功了!"); })
  .fail(function(){ alert("出错啦!"); });
  d.resolve(); // 此时,这个语句是无效的

不过更好的写法是把dtd对象变成wait()函数的内部对象

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("出错啦!"); });

此文章摘自阮一峰的网络日志 ,链接:http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值