jQuery中的deferred对象

2017年04月08日Web前端

deferred对象,在工作后才渐渐的用到了他。现在,是时候来理一遍了。

一、deferred对象

1.简单操作

deferred对象,表面上的意思是,延迟对象。他将耗时操作(ajax)与回调解耦合,可以提供更灵活的回调函数。并且该对象支持多个回调绑定多个任务。 当我们使用ajax的时候,比较传统的写法是:

$.ajax({
    url: "hello.html"
    success : function (data) { 
        alert('success');
    },
    error : function () { 
        alert('error'); 
    }
});

success和error分别是成功和失败的回调函数。

$.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象。如果高于1.5.0版本,返回的是deferred对象,我们可以进行链式操作。 所以,我们可以写成这样:

$.ajax({url: "hello.html"}).done(function() {
    alert('success');
}).fail(function(){ 
    alert('error');
});

此时,done函数就相当与success函数,fail函数就相当与error函数。

2.链式操作

就像我们操作DOM的属性那样,deferred对象也是支持链式操作的。

$.ajax({url: "hello.html"}).done(function() {
    alert('success');
}).done(function(){ 
    alert('success1');
}).fail(function(){ 
    alert('error');
}).done(function(){ 
    alert('success2');
});

执行过程是按照添加的顺序。

3.多个操作添加回调

我们可以使用新的方法$.when()。

$.when(
    $.ajax("hello.html"),
    $.ajax("world.html")
).done(function(){ 
    alert('success');
}).fail(function(){ 
    alert('error'); 
});

执行对hello.htm和world.html文件的请求,如果都成功了,出发done回调,一个或全部失败则调用fail函数。

二、普通操作(非ajax)

jQuery.Deferred对象是很灵活的,他把这一套回调函数接口,从ajax操作扩展到了所有操作。即无论是任何普通的异步或同步操作,都可以使用deferred对象。

1.生成对象

var deferred = $.Deferred();
deferred.done(function(val) {
    alert(val);
});

生成一个deferred对象,并绑定了对应的函数。同ajax操作,由于返回的都是deferred对象,所以我们可以进行链式操作。

2.触发回调

var deferred = $.Deferred();
deferred.done(function(val) {
    alert('success ' + val);
}).then(function() {
    alert('then');
}).fail(function(val) {
    alert('error ' + val);
});

deferred.resolve("hello world");   // 触发成功回调
// deferred.reject("hello world"); // 触发失败回调

当我们调用resolve()方法时,就会去执行done和then的回调函数。而reject()方法则会触发fail和then方法的调用。

注:以上代码当调用reject时,只会触发fail,是因为then中的参数是多个函数,而第一个参数是成功回调,第二个参数是失败回调 ,所以再写第二个参数就会被当成error一样回调。

3.对象状态

利用deferred对象的state方法,我们可以得到目前的状态。

var deferred = $.Deferred();
console.log(deferred.state()); // "pending"
deferred.resolve();
console.log(deferred.state()); // "resolved"

该方法返回值共有3种:

  • pending:表示操作还没有完成。
  • resolved:表示操作成功。
  • rejected:表示操作失败。

4.progress()与notify()

有时,我们需要再未改变状态时,触发某个动作时,progress与notify就有用了。

var deferred = $.Deferred();
deferred.progress(function() {
    alert(111);
});
deferred.notify();
deferred.notify();
deferred.notify();
console.log(deferred.state()); // "pending"

我们可以多次出发notify()方法,然后这个deferred对象的状态仍然是pending。但当调用了reject或resolve后,notify就不能再次调用了。

5.then()方法

then()的作用也是指定回调函数,它可以接受三个函数参数。第一个参数是resolve时调用的回调函数,第二个参数是reject时调用的回调函数,第三个参数是progress()方法调用的回调函数。

语法:deferred.then( doneFilter [, failFilter ] [, progressFilter ] )

在jQuery 1.8之后,then()返回一个新的deferred对象,而done()返回的是原有的deferred对象。如果then()指定的回调函数有返回值,该返回值会作为参数,传入后面的回调函数。看以下例子:

var deferred = $.Deferred();
deferred.done(function(val) {
    console.log(val); // 0
    return ++val;
}).then(function(val) {
    console.log(val); // 0
    return ++val;
}).done(function(val) {
    console.log(val); // 1
    return ++val;
}).then(function(val) {
    console.log(val); // 1
    return ++val;
});
deferred.resolve(0);

6.always()方法

无论是resolve或reject,都会调用always()方法。

7.promise对象

一般情况下,我们不希望用户改变deferred对象的状态。所以,我们可以在deferred对象的基础上,返回一个针对它的promise对象。我们可以通过promise对象,为原始的deferred对象添加回调函数,查询它的状态,但是无法改变它的状态,也就是说promise对象不允许你调用resolve和reject方法。

var deferred = $.Deferred();
var promise = deferred.promise();
promise.resolve();  // error

很显然,第三行就报错了。

jQuery的ajax() 方法返回的就是一个promise对象。此外,Animation类操作也可以使用promise对象。

8.when()与then()

我们已经知道$.when能够接受多个deferred对象,且只有都成功的情况下才触发done。

$.when(
    $.ajax("hello.html"),
    $.ajax("world.html"),
    $.ajax("aaa.html")
).then(successFun, errorFun);

当然,when执行了多少个deferred对象时,就有多少个参数。arg0、arg1和arg2分别对应3个ajax的返回结果。

$.when(
    $.ajax("hello.html"),
    $.ajax("world.html"),
    $.ajax("aaa.html")
).then(function(arg0, arg1, arg2) {
    console.log(arg0);
    console.log(arg1);
    console.log(arg2);
});

when方法的另一个作用是,当他的参数返回的不是一个Deferred或Promise对象,那么when方法的回调函数将立即执行。

$.when(true).done(function(val) {
    console.log(val); // true
});

三、总结

deferred对象的一些方法。

jQuery.Deferred()

创建一个新的Deferred对象的构造函数,可以带一个可选的函数参数,它会在构造完成后被调用。

jQuery.when()

通过该方式来执行基于一个或多个表示异步任务的对象上的回调函数

jQuery.ajax()

执行异步Ajax请求,返回实现了promise接口的jqXHR对象

deferred.then( doneFilter [, failFilter ] [, progressFilter ] )

当Deferred(延迟)对象解决,拒绝或仍在进行中时,调用添加处理程序。

deferred.done()

当延迟成功时调用一个函数或者数组函数.

deferred.fail()

当延迟失败时调用一个函数或者数组函数.。

deferred.always()

当Deferred(延迟)对象解决或拒绝时,调用添加处理程序。

deferred.resolve(ARG1ARG2,…)

调用Deferred对象注册的‘done’回调函数并传递参数

deferred.resolveWith(contextargs)

调用Deferred对象注册的‘done’回调函数并传递参数和设置回调上下文

deferred.isResolved

确定一个Deferred对象是否已经解决。

deferred.reject(arg1arg2,…)

调用Deferred对象注册的‘fail’回调函数并传递参数

deferred.rejectWith(contextargs)

调用Deferred对象注册的‘fail’回调函数并传递参数和设置回调上下文

deferred.promise()

返回promise对象,这是一个伪造的deferred对象:它基于deferred并且不能改变状态所以可以被安全的传递

以下列下可触发回调的动作: Callbacks Events→ ↓

deferred.always()

deferred.done()

deferred.fail()

deferred.progress()

deferred.notify() 触发 deferred.notifyWith()

触发

deferred.reject()

触发

触发

deferred.rejectWith()

触发

触发

deferred.resolve()

触发

触发

deferred.resolveWith()

触发

触发