jQuery - Deferred 对象使用

邵繁
2023-12-01


阅读了阮一峰老师的jQuery的deferred对象详解,自己整理一下。


0. jQuery Deferred对象背景


随着WEB单页应用的普及,现在越来越多的页面逻辑已经交由JS代码来处理,其中难免有向后台请求资源等费时操作。

由于浏览器是单线程执行JS代码, 所以为了防止在请求的同时页面出现卡死的现象,提出了异步请求(ajax)。

我们可以将渲染返回数据的操作在异步请求的回调中执行, 避免页面卡死。

jQuery的ajax方法最典型的回调方法就是success和error,已经相当常用了。

而Deferred对象提供了更方便更强大的方法控制回调的逻辑, Deferred对象是早在jQuery1.5版本时就引入的特性(2011年),支持jQuery的链式调用语法。


1. 什么是Deferred对象?


简单理解就是: Deferred的实例可以注册多个callback函数到一个队列中去,并根据Deferred实例的状态来有选择的,顺序的执行这些回调函数。

可以通过调用jQuery.Deferred()方法来获得一个Deferred Object实例。

var d = jQuery.Deferred() // create Deferred Object

Deferred对象有三种执行状态:

1. pending

2. resolved

3. rejected

可以在Deferred对象上调用state()方法获取。

三种状态代表的含义见名知意。


2. 主要方法: resolve(), reject()


deferred.resolve()

创建一个Deferred对象,Deferred对象默认执行状态为“pending‘”,
延迟一小段时间模拟异步操作过程,异步操作完成后,在Deferred对象上调用resolve()方法, 
Deferred对象执行状态变为“resolved”
  var d = jQuery.Deferred() // create Deferred Object
  console.log(d.state()); // return 'pending'
  function executeTasks() { 
    var _tasks = function() { 
    
    // 'finish tasks here'
    d.resolve(); // notify Deferred Object has been resolved
    console.log(d.state()); // return 'resolved' 
    } 
    setTimeout(_tasks, 3000); 
   }
  executeTasks();

deferred.reject()

如果异步操作过程失败,可以在Deferred对象上调用reject()方法,
Deferred对象执行状态变为“rejected”.
  var d = jQuery.Deferred() // create Deferred Object


  function executeTasks() {
    var _tasks = function() {

      // 'finish tasks here'

      d.reject(); // notify Deferred Object has been rejected
      console.log(d.state());  // return 'rejected'
    }
    setTimeout(_tasks, 3000);
  }
  executeTasks();


3. 主要事件: done(), fail(), always()

deferred.done()

Deferred对象获得状态“resolved”后,执行done()回调。
可以自由添加多个done回调函数,如下。
  var d = jQuery.Deferred() // create Deferred Object
  d.done(function(){
    console.log('I am done. n.n'); // 1
  }).done(function(){
    console.log('I am done again. n.n'); // 2
  })

  function executeTasks() {
    var _tasks = function() {
      
      // 'finish tasks here'

      d.resolve(); // notify Deferred Object has been resolved
    }
    setTimeout(_tasks, 3000);
  }

  executeTasks();

deferred.fail()

Deferred对象获得状态“rejected”后,执行fail()回调。
因为Deferred对象支持链式调用,所以可以直接在注册done回调之后直接注册fail()回调。
  var d = jQuery.Deferred() // create Deferred Object
  d.done(function(){
    console.log('I am done. n.n'); // 1
  }).done(function(){
    console.log('I am done again. n.n'); // 2
  }).fail(function(){
    console.log('I am rejected. >.<');
  })

  function executeTasks() {
    var _tasks = function() {
      
      // 'finish tasks here'

      d.reject(); // notify Deferred Object has been rejected
    }
    setTimeout(_tasks, 3000);
  }

  executeTasks();


deferred.always()

always函数顾名思义,无论Deferred对象最终获得resolved状态还是rejected状态,该回调函数总是会被调用。

  var d = jQuery.Deferred() // create Deferred Object
  d.fail(function(){
    console.log('I am rejected. >.<');
  }).always(function(){
    console.log('I am always here. =.= ');
  });

  function executeTasks() {
    var _tasks = function() {

      // 'finish tasks here'

      d.reject(); // notify Deferred Object has been rejected
      d.state();  // return 'rejected'
    }
    setTimeout(_tasks, 3000);
  }
  executeTasks();


4. 为多个操作指定回调函数


$.when()

when()方法使得Deferred对象支持多个异步操作都完成后,才执行统一定义的回调。

var d1 = $.Deferred();
var d2 = $.Deferred();
var d3 = $.Deferred();

$.when( d1, d2, d3 ).done(function ( v1, v2, v3 ) {
    console.log( v1 ); // v1 is undefined
    console.log( v2 ); // v2 is "abc"
    console.log( v3 ); // v3 is an array [ 1, 2, 3, 4, 5 ]
});

d1.resolve();
d2.resolve( "abc" );
d3.resolve( 1, 2, 3, 4, 5 );

假若所有Deferred对象获得resolved状态,执行done回调。

假若其中至少一个Deferred对象获得rejected状态,执行fail回调。


5. Defferred和promise()


deferred.promise()

promise()方法返回一个和原Deferred对象几乎相同的代理对象,
只不过该代理对象只允许attach事件如done, fail和always,
但不允许调用改变执行状态的方法如resolve和reject。
function getPromise() {
    return $.Deferred().promise();
}

try {
    getPromise().resolve("a");
} catch(err){
    console.log(err);
}
最常用的应用是jQuery的ajax方法,该方法返回的就是一个Deferred对象的Promise()对象。
$.ajax("/home/news/")
.done(function(){ alert('I am done. n.n');} )
.fail(function(){ alert('I am rejected. >.<'); } )
这样做的好处是避免在Deferred对象的创建者的作用范围外改变Deferred对象的执行状态。


 类似资料: