jQuery的deferred对象详解

转自:“阮一峰”:http://www.ruanyifeng.com/blo…

一、什么是deferred对象?
开辟网站的过程当中,我们常常碰到某些耗时很长的javascript操纵。个中,既有异步的操纵(比方ajax读取服务器数据),也有同步的操纵(比方遍历一个大型数组),它们都不是马上能获得效果的。
一般的做法是,为它们指定回调函数(callback)。即事前划定,一旦它们运转终了,应当挪用哪些函数。
然则,在回调函数方面,jQuery的功用异常弱。为了转变这一点,jQuery开辟团队就设想了deferred对象。
简单说,deferred对象就是jQuery的回调函数处置惩罚方案。在英语中,defer的意义是”耽误”,所以deferred对象的寄义就是”耽误”到将来某个点再实行。
它处置惩罚了如何处置惩罚耗时操纵的题目,对那些操纵供应了更好的掌握,以及一致的编程接口。它的主要功用,能够归结为四点。下面我们经由过程示例代码,一步步来进修。

二、ajax操纵的链式写法
起首,回忆一下jQuery的ajax操纵的传统写法:

$.ajax({
    url: "test.html",
    success: function(){
      alert("哈哈,胜利了!");
    },
    error:function(){
      alert("失足啦!");
    }
  });
  

在上面的代码中,$.ajax()接收一个对象参数,这个对象包括两个要领:success要领指定操纵胜利后的回调函数,error要领指定操纵失利后的回调函数。
$.ajax()操纵完成后,假如运用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法举行链式操纵;假如高于1.5.0版本,返回的是deferred对象,能够举行链式操纵。
如今,新的写法是如许的:

$.ajax("test.html")
  .done(function(){ alert("哈哈,胜利了!"); })
  .fail(function(){ alert("失足啦!"); });
  

能够看到,done()相当于success要领,fail()相当于error要领。采纳链式写法今后,代码的可读性大大提高。

三、指定统一操纵的多个回调函数
deferred对象的一大长处,就是它许可你自在增加多个回调函数。
照样以上面的代码为例,假如ajax操纵胜利后,除了本来的回调函数,我还想再运转一个回调函数,怎么办?
很简单,直接把它加在背面就好了。

$.ajax("test.html")
  .done(function(){ alert("哈哈,胜利了!");} )
  .fail(function(){ alert("失足啦!"); } )
  .done(function(){ alert("第二个回调函数!");} );
  

回调函数能够增加恣意多个,它们依据增加递次实行。

四、为多个操纵指定回调函数
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对象的种种要领,指定回调函数。

我们来看一个详细的例子。假定有一个很耗时的操纵wait:

 var wait = function(){
    var tasks = function(){
      alert("实行终了!");
    };
    setTimeout(tasks,5000);
  };

我们为它指定回调函数,应当怎么做呢?
很天然的,你会想到,能够运用$.when()

$.when(wait())
  .done(function(){ alert("哈哈,胜利了!"); })
  .fail(function(){ alert("失足啦!"); });
  

然则,如许写的话,done()要领会马上实行,起不到回调函数的作用。缘由在于$.when()的参数只能是deferred对象,所以必需对wait()举行改写:

var dtd = $.Deferred(); // 新建一个deferred对象
  var wait = function(dtd){
    var tasks = function(){
      alert("实行终了!");
      dtd.resolve(); // 转变deferred对象的实行状况
    };
    setTimeout(tasks,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.reject()要领,作用是将dtd对象的实行状况从”未完成”改成”已失利”,从而触发fail()要领。

var dtd = $.Deferred(); // 新建一个Deferred对象
  var wait = function(dtd){
    var tasks = function(){
      alert("实行终了!");
      dtd.reject(); // 转变Deferred对象的实行状况
    };
    setTimeout(tasks,5000);
    return dtd;
  };
  $.when(wait(dtd))
  .done(function(){ alert("哈哈,胜利了!"); })
  .fail(function(){ alert("失足啦!"); });
  

七、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(); // 此时,这个语句是无效的

在上面的这段代码中,wait()函数返回的是promise对象。然后,我们把回调函数绑定在这个对象上面,而不是本来的deferred对象上面。如许的长处是,没法转变这个对象的实行状况,要想转变实行状况,只能操纵本来的deferred对象。

不过,更好的写法是allenm所指出的,将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("失足啦!"); });
  

八、一般操纵的回调函数接口(中)

另一种防备实行状况被外部转变的要领,是运用deferred对象的建构函数$.Deferred()。
这时候,wait函数照样坚持稳定,我们直接把它传入$.Deferred():

$.Deferred(wait)
  .done(function(){ alert("哈哈,胜利了!"); })
  .fail(function(){ alert("失足啦!"); });

jQuery划定,$.Deferred()能够接收一个函数名(注重,是函数名)作为参数,$.Deferred()所天生的deferred对象将作为这个函数的默许参数   

九、一般操纵的回调函数接口(下)  

 除了上面两种要领之外,我们还能够直接在wait对象上布置deferred接口。

 var dtd = $.Deferred(); // 天生Deferred对象
  var wait = function(dtd){
    var tasks = function(){
      alert("实行终了!");
      dtd.resolve(); // 转变Deferred对象的实行状况
    };
    setTimeout(tasks,5000);
  };
  dtd.promise(wait);
  wait.done(function(){ alert("哈哈,胜利了!"); })
  .fail(function(){ alert("失足啦!"); });
  wait(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("已实行!");} );
    原文作者:小懒虫
    原文地址: https://segmentfault.com/a/1190000009116391
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞