首页 > 代码库 > Deferred

Deferred

上文中提到了Promise构造函数,就不得不提deferred(延迟)对象了,其实,让deferred对象得到推广的便是其在jQuery中的使用,本文就jQuery中对deferred对象的使用进行探讨。

延迟对象,在jQuery的1.5引入,是通过调用jQuery.Deferred()方法创建一个可链式调用的工具对象。 它可以注册多个回调到回调队列, 调用回调队列,准备代替任何同步或异步函数的成功或失败状态。

延迟对象是可链式调用的,类似于jQuery对象是可链接的方式,  但它有其自己的方法。 创建一个Deferred(延迟)对象后,你可以通过使用下面任何方法 直接从对象创建或者链式调用 或 保存对象的一个变量,调用该变量的一个或多个方法。

 jQuery.Deferred()基于Promises/A规范实现,因为jQuery本身的设计风格,jQuery.Deferred()并没有完全遵循Promises/A规范。
jQuery在1.5版本中首次引入了Deferred。你可以通过jQuery.Deferred() 在未来某个时候 得到 ‘延时’返回值。 在此之前是无法单独使用的,Deferred作为对ajax模块较大重写的一部分添加进来。1.5和1.6版本包含deferred功能,可以使$.ajax() 接收调用完成及请求出错的回调,但却存在严重的耦合。新版本的jQuery提供了一些增强的方式来管理回调,提供更加灵活的方式建立回调,而不用关心原始的回调是否已经触发。 jQuery的Deferred对象支持多个回调绑定多个任务,任务本身既可以是同步也可以是异步的。

 先看看$.Deferred是 什么?

技术分享

可以看出Deferred只是一个普通函数,没有太多的信息,那我们调用一下试试。

技术分享

我们又可以看出,其实Deferred函数其实返回的是一个对象,这个对象也有always、catch、done、fail、notity、notifyWith、pipe、promise、progress、reject、rejectWith、resolve、resolveWith、state、then方法。

Deferred有三个状态,pending:未完成,resolved:操作成功,rejected: 操作失败

当deferred1对象调用resolved时,deferred1中的done方法会被调用,当deferred1对象调用rejected时,deferred1的fail方法会被调用,比如

var deferred1 = $.Deferred();
deferred1.done(function (data) {
    console.log(‘success: ‘,  data)
})
deferred1.fail(function (err) {
    console.log(‘err: ‘, err)
})
setTimeout(function(){
  deferred1.resolve(‘victory‘)
},200)

//输出success: victory

我们接着聊notify和progress。

progress()用来指定一个回调函数,当调用notify()方法时,该回调函数将执行。它的用意是提供一个接口,使得在非同步操作执行过程中,可以执行某些操作,比如定期返回进度条的进度。

var userProgress = $.Deferred();
var $profileFields = $("input");
var totalFields = $profileFields.length;
userProgress.progress(function (filledFields) {
    var pctComplete = (filledFields/totalFields)*100;
    $("#progress").html(pctComplete.toFixed(0));
}); 
userProgress.done(function () {
    $("#thanks").html("Thanks for completing your profile!").show();
});
$("input").on("change", function () {
    var filledFields = $profileFields.filter("[value!=‘‘]").length;
    userProgress.notify(filledFields);
    if (filledFields == totalFields) {
        userProgress.resolve();
    }
});

现在接着聊then方法,then方法接收三个回调函数,第一个参数是resolve时调用的回调函数,第二个参数是reject时调用的回调函数,第三个参数是progress()方法调用的回调函数。

 

和谈Promise一样,现在我们也Deferred来封装一个getJSON函数,实现AJAX的例子

var getJSON = function(url) {
    var deferred = $.Deferred();var client = new XMLHttpRequest();
    client.open(‘GET‘, url);
    client.onreadystatechange = function(){
        if (this.readyState !== 4) {
            return
        }
        if (this.status === 200) {
            deferred.resolve(this.response)
        } else {
            deferred.reject(new Error(this.statusText))
        }
    }   
    return deferred
}
getJSON(‘/db.json‘).then(function(json){
    console.log(‘Result:‘,  json)    
}, function(error){
    console.log(‘error:‘, error)
})

promise对象

大多数情况下,我们不想让用户从外部更改deferred对象的状态。这时,你可以在deferred对象的基础上,返回一个针对它的promise对象。我们可以把后者理解成,promise是deferred的只读版,或者更通俗地理解成promise是一个对将要完成的任务的承诺。

你可以通过promise对象,为原始的deferred对象添加回调函数,查询它的状态,但是无法改变它的状态,也就是说promise对象不允许你调用resolve和reject方法。

function getPromise(){
    return $.Deferred().promise();
}
try{
    getPromise().resolve("a");
} catch(err) {
    console.log(err);
}
//会进入catch方法,显示TypeError {}

jQuery的ajax() 方法返回的就是一个promise对象。

 

Deferred