• 【转】$.ajax()引发的对Deferred的总结


    转自:http://www.cnblogs.com/guoyansi19900907/p/5000267.html

    传统的ajax写法:

    $.ajax({
        url:"1.json",
        type:"get",
        success:function(data){},
        error:function(){}
    });

    jquery 1.7以后的新写法,

    复制代码
    $.ajax({
        url:"1.json",
        type:"get"    
    }).done(function(data){
        
    }).fail(function(){
        
    });
    复制代码

    我就纳闷了.$.ajax()返回的是XMLHttpRequest对象.

    我们都知道XMLHttpRequest是ajax的一个核心对象,用于和服务器交互的, 可是XMLHttpRequest对象根本就没有什么done,fail方法, 这里的方法是怎么加上去的呢? 我们从done入手.在官网api上搜索done.

    发现一个Deferred关键词.

    我们现在就从jQuery的Deferred开始讲起吧.

    Deferred对象是jquery开发团队设计的,为了增强jquery的回调功能,在1.7版本中加入了jquery. defer英语意思是延迟的意思.那么他是如何延迟的呢? 首先来理解一下基本知识,要不然后面没法说了. $.Deferred([fun]) $.Deferred()没有参数时 返回Deferred对象; 有参数时,表示在这个参数上做延迟或异步操作,并且返回Deferred对象. done(fn)        当延迟成功后调用该方法 fail(fn)        当延迟失败时调用失败 then(done,fail)        这个是done和fail的总写方式 always(fn)        不管延迟执行的成功还是失败,都执行的方法 上面的fn可能是一个function,也有可能是多个以逗号分隔的function函数
    resolve和reject方法一旦执行,表示开始执行done,fail,then,always方法, 注意Deferred对象可以一次挂接多个done,fail方法,按照你分布的顺序依次执行.
    resolve(value)        告诉对象执行done回调,value是参数 reject(value)        告诉对象执行fail回调,value是参数. 调用done回调代码:

    var dfd = $.Deferred();
    dfd.done(function(value) {
        alert(value);
    }); dfd.resolve("执行done回调"); 

    调用fail回调代码:

    var dfd = $.Deferred();
    dfd.done(function(value) {
        alert(value);
    });
    dfd.reject("执行fail回调");

    调用then回调代码:

    复制代码
    var dfd = $.Deferred();
    var doneFun=function(value) {
        alert("done:"+value);
    };
    var failFun=function(value) {
        alert("fail:"+value);
    }
    dfd.then(doneFun,failFun);
    dfd.reject("思思博士");
    复制代码

    调用always回调代码:

    复制代码
    var dfd = $.Deferred();
    var doneFun = function(value) {
        alert("done:" + value);
    };
    var failFun = function(value) {
        alert("fail:" + value);
    }
    dfd.then(doneFun, failFun).always(function() {
        alert("不管你延迟成功还是失败,我都要执行always.");
    });
    dfd.resolve("我要执行done,但是这不影响我执行always!");
    复制代码

    state()        返回deferred对象目前的状态

    1.pending:操作没有完成 2.resolved:操作成功 3.rejected:操作失败 代码:

    复制代码
    var dfd=$.Deferred();
    dfd.done(function(){
        console.log("done");
    });
    console.log(dfd.state());
    dfd.resolve();
    console.log(dfd.state());
    复制代码

    上面我们说了:resolve表示立马执行回调, 所以这个地方的弹出是这样的

    黄金搭档 progress(fn)和notify(value)

    progress和done之类的方法截然不同. progress(fn)的fn表示一个回调函数,这个回调函数由notify()方法通知执行. 代码:

    复制代码
    var dfd=$.Deferred();
    dfd.progress(function(data){
        alert(data);
    }).done(function(data){
        alert("done:>>>>>"+data);
    }).fail(function(data){
        alert("fail:>>>>"+data);
    });
    function getProcess(){
        dfd.notify("我是progress回调函数的参数");
        var a=true;
        //下面判断是为了执行done还是fail
        if(a){
            dfd.resolve("执行done.....");
        }else{
            dfd.reject("执行fail......");
        }
    }
    复制代码
    <input type="button" value="确定" onclick="getProcess()" />

    根据以上分析,Deferred对象才有done这样的反法, 根据$.ajax().done()推测$.ajax()返回的应该是Deferred对象? 姑且认为这个结论是对的吧. 那么jQuey除了$.ajax()有done方法外,还有哪些东西有done方法. $.when(d);d是一个或多个延迟对象,或者普通的JavaScript对象。 下面看一下参数是一个普通对象的.

    $.when({address:"china",sex:"男"})
    .done(function(data){
        console.log(data.address)
    });

    这个例子主要是为了演示参数的传递的 前面说了同一延迟的多个回调的两种写法 看下面:

    1.

    $.ajax("1.json")
    .done(function(data){
        alert("done1")
    }).done(function(){
        alert("done2");
    });

    2.

    $.ajax("1.json")
    .done(function(data){
        alert("done1")
    },function(){
        alert("done2");
    });

    结合when用法

    $.when($.ajax("1.json"))
    .done(function(data){
        console.log(data);
    });

    现在说一说多个延迟的同一个调用

    复制代码
    $.when($.ajax("1.json"), $.ajax("2.json"),$.ajax("3.json"))
    .done(function(data1, data2,data3) {
        console.log(data1);
        console.log(data2);
        console.log(data3);
    }).fail(function(data1,data2,data3){
        console.log(data1);
        console.log(data2);
        console.log(data3);
    });
    复制代码

    这个地方返回值有点臃肿,他把$.ajax()对象给返回回来了 这个地方的用的是ajax做的延迟, 那么我们能不能用setTimeout模拟延迟

    复制代码
    function delay(){
        setTimeout(function(){
            alert("执行啦");
        },2000);
    }        
    $.when(delay())
    .done(function(){
        alert("done");
    });
    复制代码

    不对,这个地方首先执行的是done,2s后执行的才是setTimeout. 思考思考,想一想上面的参数. 这个地方最多只是将delay()当做参数传递给done回调函数的参数.因为不是一个Deferred类型的参数. 因为$.ajax()返回Deferred类型才能使用done一样. 那么要怎么样才能让delay返回一个Deferred对象来完成我们的延迟模拟呢. 现在让我们回想一下上面的第一串代码$.Deferred(); 这个可以返回Deferred类型. 改写上面代码:

    复制代码
    var dfd=$.Deferred();        
    function delay(dfd){
        var bool=true;
        setTimeout(function(){
            alert("delay的setTimeout执行啦!");
            if(bool){
                dfd.resolve("done");    
            }else{
                dfd.reject("fail");
            }
            
        },2000);
        return dfd;
    }
    
    $.when(delay(dfd))
    .done(function(value){
        alert(value);
    }).fail(function(value){
        alert(value);
    });
    复制代码

    这个时候我们终于实现了延迟,回到功能. 上面说了 done只要一遇到resolve或fail遇到reject就会立即执行, 那么我们在底部添加一行代码: 修改上面的代码:

    复制代码
    var dfd=$.Deferred();        
    function delay(dfd){
        var bool=true;
        setTimeout(function(){
            console.log("delay的setTimeout执行啦!");
            if(bool){
                dfd.resolve("done");    
            }else{
                dfd.reject("fail");
            }
            
        },2000);
        return dfd;
    }
    
    $.when(delay(dfd))
    .done(function(value){
        console.log("done1"+value);            
    }).fail(function(value){
        console.log("fail1"+value);
    });
    dfd.resolve("我是来捣乱的....");
    复制代码

    顺序变成了这样:

    很明显破坏了我们模拟延迟的目的. 如何解决 将dfd从全局改成局部变量,这样别人就不能轻易的改变状态了.

    复制代码
    function delay(){
        var dfd=$.Deferred();    
        var bool=true;
        setTimeout(function(){
            alert("delay的setTimeout执行啦!");
            if(bool){
                dfd.resolve("done");    
            }else{
                dfd.reject("fail");
            }
        },2000);
        return dfd;
    }
    $.when(delay())
    .done(function(value){
        alert("done1"+value);            
    }).fail(function(value){
        alert("fail1"+value);
    });
    复制代码

     还是那句话:done只要一遇到resolve或fail遇到reject就会立即执行  改写上面的代码:

    复制代码
    function delay(){
        var dfd=$.Deferred();    
        var bool=true;
        setTimeout(function(){
            console.log("delay的setTimeout执行啦!");
            if(bool){
                dfd.resolve("done");    
            }else{
                dfd.reject("fail");
            }
        },2000);
        return dfd;
    }
    var delay2=delay();
    delay2.resolve();
    $.when(delay())
    .done(function(value){
        console.log("done1"+value);            
    }).fail(function(value){
        console.log("fail1"+value);
    });
    复制代码

    这个时候执行变成这样,2s后:

    如果我们的Deferred可以这样任意的被篡改,那么我们的程序健壮何在, 有没有一个办法让外人无法访问到Deferred对象,但是又不影响我们的回调函数的调用. 答案是有:promise(); 改写代码:

    复制代码
    function delay(){
        var dfd=$.Deferred();    
        var bool=true;
        setTimeout(function(){
            console.log("delay的setTimeout执行啦!");
            if(bool){
                dfd.resolve("done");    
            }else{
                dfd.reject("fail");
            }
        },2000);
        return dfd.promise();
    }
    //var delay2=delay();
    //delay2.resolve();
    $.when(delay())
    .done(function(value){
        console.log("done1"+value);            
    }).fail(function(value){
        console.log("fail1"+value);
    });
    复制代码

    这个时候你在把注释放开,就会报错了.

    在看下面这串代码的报错: $.ajax().resolve();

    是不是很熟悉的报错. 所以我们的$.ajax()更确切的说最终返回的是Promise对象. $.ajax(),$.when()说白了不过是一个方法而已. 既然jQuery能够搞个$.ajax(),$.when()接口出来,我们理所当然的也能搞一个 这样的接口出来了. 回到最上面的那串代码: $.Deferred(fn); 我们一直都是在用无参的$.Deferred(); 那这个fn参数是干嘛的呢?fn主要用来部署异步或延迟操作的.

    复制代码
     $.delay=$.Deferred(function(dfd){
        setTimeout(function(){
            alert("setTimeout执行啦!!!!");
            dfd.resolve();
        },2000);
    });
    $.delay.done(function(){
        alert("done1");
    });
    复制代码

    注意:这里的dfd不需要我们调用时传递,自己会传一个Deferred对象进去 这样看上去是不是就和$.ajax()和$.when()差不多了. 但是又不太一样,$.ajax()和$.when()可以传参数我们的$.delay不能传递参数,他也不是一个方法,就是一个Promise对象. 继续优化上面的代码,使之可以传递参数:

    复制代码
    $.delay = function(time) {
        return $.Deferred(function(dfd) {
            setTimeout(function() {
                alert("setTimeout执行啦!!!!");
                dfd.resolve();
            }, time);
        })
    }
    $.delay(5000).done(function() {
        alert("done1");
    });
    复制代码

    代码优化好了之后,功能实现了; 此时问题又来了.$.delay()是jQuery已经定义过的一个方法,我们定义的方法额jQuery重名了 不如我们吧$.delay()改成一个普通的方法,不更好.

    复制代码
    var delay = function(time) {
        return $.Deferred(function(dfd) {
            setTimeout(function() {
                alert("setTimeout执行啦!!!!");
                dfd.resolve();
            }, time);
        })
    }
    delay(5000).done(function() {
        alert("done1");
    });
    复制代码

    或者是这样:

    复制代码
    function delay(time) {
        return $.Deferred(function(dfd) {
            setTimeout(function() {
                alert("setTimeout执行啦!!!!");
                dfd.resolve();
            }, time);
        })
    }
    delay(5000).done(function() {
        alert("done1");
    });
    复制代码

    上面说的是通过返回Promise对象使其有了done等接口, 能否直接在给出的函数上布置接口呢?这里就用到了我们之前用到的promise() 代码如下:

    复制代码
    var dfd=$.Deferred();
    function delay(d,time) {
        setTimeout(function(){
            alert("setTimeout执行啦!!");
            d.resolve();
        },time);
    }
    dfd.promise(delay);
    delay.done(function() {
        alert("done1");
    });
    delay(dfd,5000);
    复制代码

    这里又出现了dfd这样的全局变量,而且在调用时还是需要传递dfd参数,似乎有点不好看,优化一下:

    复制代码
    function ansy(time) {
        var dfd = $.Deferred();
        function delay(d, time) {
            setTimeout(function() {
                alert("setTimeout执行啦!!");
                d.resolve("我被done弹出来了");
            }, time);
        }
        dfd.promise(delay);
        delay(dfd,time);
        return delay;
    }
    
    ansy(2000).done(function(value){
        alert("done说:"+value);
    });
    复制代码

    好了总算说完了. 现在我们看看jQuery1.7.2的ajax源码:

    为了阅读方便,我把源码删掉了一部分,行号和真实的jquery行号对不上. ajax最终返回的是jqXHR,而在7424行在bei部署成了Promise对象. 因此一开始的疑惑终于解决了,$.ajax()返回的是Promise,并非我们在官网上看到的是XMLHttpRequest对象.

  • 相关阅读:
    github-git clone 下载很慢的问题解决
    java-springCloud环境配置
    github-上传自己的项目到github仓库
    java-项目中无法访问js、css等静态资源
    java-新建简单的Web项目
    Echarts中axislabel文字过长导致显示不全或重叠
    JS设置cookie,读取cookie,删除cookie
    整理base.css,重设浏览器样式
    IE6中png背景图片透明的最好处理方法
    PHP的报错级别并返回当前级别error_reporting()
  • 原文地址:https://www.cnblogs.com/majw/p/7153900.html
Copyright © 2020-2023  润新知