美文网首页我爱编程
对比jQuery.defered对象和原生Promise对象

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

作者: microkof | 来源:发表于2017-02-09 13:50 被阅读514次

    参考文章:
    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(),其他方法很多。所以,技术选型时:

    • 如果你确定不需要Promise.race()功能,但很需要jQ自带的一些方法全部方法看这里,那么,选jQuery.defered。

    • 如果你很需要Promise.race()功能,并不需要jQ自带的一系列方法,那么用Promise的polyfill,或原生Promise即可。

    相关文章

      网友评论

        本文标题:对比jQuery.defered对象和原生Promise对象

        本文链接:https://www.haomeiwen.com/subject/fzhiittx.html