我爱编程

对比jQuery.defered对象和原生Promise对象

2017-02-09  本文已影响514人  microkof

参考文章:
jQuery的deferred对象详解以及我写过的Promise文章
jQuery.defered最新的文档:http://www.css88.com/jqapi-1.9/category/deferred-object/

前言

jQuery跟人的第一印象就是操作DOM的库,新手学习jQuery也是先从操作DOM学起,我当年也是如此。学会了操作DOM之后再学学事件和Ajax,就以为学成了,然而jQuery.defered对象由于用处不大,我始终没有系统的学习,只是在jQuery.ajax里面间接使用过。到今天,虽然ES6和Vue等新技术很有市场,但是jQuery依然是小型项目的最佳解决方案,更何况我们开发PC页面依然必须支持IE8,我的项目也越写越复杂,所以我认为jQuery.defered对象是IE8中解决回调问题的优选之一。另一套技术方案就是用Promise的polyfill,它的优点是跟原生Promise的语法完全一致,缺点是要专门引入库。

本文只对比两种技术方案的区别,适合于已经熟悉原生Promise理念,也研究过jQuery.defered对象的人阅读。

链式操作举例

找出我从前的一个例子(原文),隔一段时间打印一些字符。原生写法如下:

var promise = new Promise(function(resolve, reject) {
    setTimeout(function() {
        console.log('第一个回调');
        resolve(3);
    }, 3000);
});

promise.then(function(value) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            console.log('第二个回调');
            console.log(value * 2);
            resolve(value * 2);
        }, 2000);
    });
}).then(function(value) {
    setTimeout(function() {
        console.log('第三个回调');
        console.log(value * 2);
    }, 1000);
});

也就是先new一个Promise对象,然后在这个Promise对象身上加then方法。

jQuery.defered()方案写法如下:

var dfd = function() {
    var deferred = $.Deferred();
    setTimeout(function() {
        console.log("执行完毕1");
        deferred.resolve(1);
  },4000);
    return deferred;
};

$.when(dfd())
.then(function(value){
    var deferred = $.Deferred();
    setTimeout(function() {
        console.log("执行完毕2");
        console.log('value = ' + value);
        deferred.resolve(value + 1);
    },3000);
    return deferred;
})
.then(function(value){
    var deferred = $.Deferred();
    setTimeout(function() {
        console.log("执行完毕3");
        console.log('value = ' + value);
        deferred.resolve(value + 1);
    },2000);
    return deferred;
});

首先研究一下$.when()(文档

这个方法的正确使用姿势是:传入0个或者多个延迟对象。当全部成功,则执行.done,如果有一个失败,就执行.fail。

在多个延迟对象传递给jQuery.when() 的情况下,.when()根据一个新的“宿主” Deferred(延迟)对象,跟踪所有已通过Deferreds聚集状态,返回一个Promise对象。

当所有的延迟对象被解决(resolve)时,“宿主” Deferred(延迟)对象才会解决(resolved)该方法,或者当其中有一个延迟对象被拒绝(rejected)时,“宿主” Deferred(延迟)对象就会reject(拒绝)该方法。

如果“宿主” Deferred(延迟)对象是解决(resolved)状态时, “宿主” Deferred(延迟)对象的 doneCallbacks (解决回调)将被执行。参数传递给 doneCallbacks提供这解决(resolved)值给每个对应的Deferreds对象,并匹配Deferreds传递给 jQuery.when()的顺序。

再看看构造延迟对象的区别

从书写上,两个例子略有区别,jq的写法是以普通函数return延迟对象,原生写法是用构造函数直接构造延迟对象。那么jq有没有直接构造延迟对象的办法?有。

下面就是以构造函数$.Deferred()直接构造延迟对象的例子,构造函数会给回调函数的参数传入一个空延迟对象,所以内部不需要var deferred = $.Deferred();。then里的代码完全相同。

$.Deferred(function(dfd){
    setTimeout(function(){
        console.log("1执行完毕!");
        dfd.resolve(1);
    },4000);
    return dfd;
})
.then(function(value){
    var deferred = $.Deferred();
    setTimeout(function(){
        console.log("2执行完毕!");
        deferred.resolve(value + 1);
    },3000);
    return deferred;
})
.then(function(value){
    var deferred = $.Deferred();
    setTimeout(function(){
        console.log("3执行完毕!" + value);
        deferred.resolve();
    },2000);
    return deferred;
});

这两种的代码量、理解难易度都差不多,如果你的“第一步操作”只需要创建一个延迟对象,那么用谁都可以,如果你的“第一步操作”需要创建多个延迟对象,而且这些延迟操作是并发关系,那么只能用$.when(),因为它可以接受多个延迟对象,且会更待所有的操作完成再做出响应。

jQuery.defered对象和原生Promise对象对应关系总结

以下左为jQuery.defered对象,右为原生Promise对象

$.Deferred()类似于new Promise()

deferred.then没有对应任何原生Promise方法,因为deferred.then可以有三个处理程序,第一个是成功后的,第二个是失败后的,第三个是[可选]当Deferred(延迟)对象生成进度通知时被调用的一个函数。也就是说,deferred.then = deferred.done + deferred.fail + progressFilter。

deferred.then是jQuery中唯一可以传递延迟状态的方法。其实在1.8版本之前,没有任何方法可以传递延迟状态,只不过1.8版本开始,jQ的开发者们给then赋予了传递延迟状态的能力,这时候,很多使用者误以为done和fail等也可以传递延迟状态,其实并不是,记住,直至本文完稿为止,只有then方法有这个能力。

再具体说,就是then方法后面可以接done、fail等方法,then可以把延迟状态传递给done和fail等方法,但是,done、fail方法后面不可以接then方法,说白了done、fail就是延迟链的终点,不会再有延迟对象传递。这跟原生Promises是不一样的。

deferred.done也没有对应任何原生Promise方法,千万不要以为deferred.done对应单参数的new Promise().then,因为deferred.done连缀书写的话,各个回调函数是并列关系,回调函数里面如果有异步任务,会导致执行顺序不符合你的期待。

deferred.fail跟deferred.done同理,不要以为deferred.fail对应new Promise().catch。而且,new Promise().catch后面可以继续传递延迟状态,但是deferred.fail不可以。

看了上文,你就也可以知道,deferred.catch也不对应new Promise().catch。deferred.catch是deferred.then( null, fn )的别名,deferred.catch跟deferred.fail的区别在于deferred.fail更强大,可以接受多个函数。

deferred.always也没有对应方法,因为always可以传入处理程序列表,但是这些处理程序是同类并列关系,并不是说前一个函数对应成功,后一个函数对应失败。所以jQuery官方也建议,不要去用if判断上一步到底是成功还是失败,而是应当执行一些无状态的语句,这样才符合always单词的语义。如果想判断,依然推荐你用done和fail。

$.when()对应Promise.all()

没有方法对应Promise.race()

剩余其他deferred方法用得少,暂不举例

现在就可以看出来各方的优势了,原生方法的优势是符合业界标准,理解简单,jQ的优势是除了没有Promise.race(),其他方法很多。所以,技术选型时:

上一篇 下一篇

猜你喜欢

热点阅读