• 【jQuery】Deferred(延迟)对象


    本文针对jQuery-todolist项目中使用到的Deferred(延迟)对象进行具体分析

    $.Deferred() 是一个构造函数,用来返回一个链式实用对象方法来注册多个回调,并且调用回调队列,传递任何同步或异步功能成功或失败的状态。 
    提示:1. $.Deferred() 构造函数创建一个新的 Deferred(延迟)对象, jQuery.Deferred 可传递一个可选的函数,该函数在构造方法返回之前被调用并传递一个新的 Deferred 对象作为函数的第一个参数。例如被调用的函数可以使用 deferred.then()来附加回调函数。
    2. 一个 Deferred 对象开始于挂起状态。任何使用 deferred.then(), deferred.always(), deferred.done(), 或者 deferred.fail() 添加到这个对象的回调函数都是排队等待执行的。调用 deferred.resolve() 或 eferred.resolveWith() 转换延迟解决状态后立即执行设置的 doneCallbacks 。调用 deferred.reject() 或 deferred.rejectWith() 转换延迟拒绝状态后立即执行设置的 failCallbacks 。一旦对象已经进入了解决或拒绝状态,它保持该状态。回调仍然可以添加到已解决或已拒绝的 Deferred 对象——它们会立即执行。

    deferred.promise() 函数返回 Deferred(延迟)的 Promise 对象

    注意:1. 方法允许一个异步函数阻止那些干涉其内部请求的进度(progress)或状态(status)的其它代码。
    2. 只包含 deferred 对象的一组方法,包括:done(),then(),fail(),isResolved(), isRejected(), always(), 这些方法只能观察一个 deferred 的状态,而无法更改 deferred 对象的内在状态。
    3. deferred.promise()也可以接受一个 target 参数,此时传入的 target 将被赋予 Promise 的方法,并作为结果返回,而不是创建一个新对象。

    $(function () { 
        function asyncEvent(){
            var dfd = new jQuery.Deferred();
            // 在一个随机的时间间隔之后 Resolve (解决状态)
            setTimeout(function(){
                dfd.resolve("欢呼");
            }, Math.floor(400+Math.random()*2000));
            // 在一个随机的时间间隔之后 reject (拒绝状态)
            setTimeout(function(){
                dfd.reject("对不起");
            }, Math.floor(400+Math.random()*2000));
            // 每半秒显示一个"working..."消息
            setTimeout(function working(){
                if ( dfd.state() === "pending" ) {
                    dfd.notify("working... ");
                    setTimeout(working, 500);
                }
            }, 1);
            // 返回 Promise 对象,调用者不能改变延迟对象
            return dfd.promise();
        }
        // 为异步函数附加一个done, fail, 和 progress 处理程序
        $.when( asyncEvent() ).then(
            function(status){
                alert( status+', 事情进展顺利' );
            },
            function(status){
                alert( status+', 这次你失败了' );
            },
            function(status){
                $("body").append(status);
            }
        );
    })

    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是参数.

    function pop(arg) {
            if(!arg){
                console.error('pop title is requried');
            }
            var conf={},
                $box,
                $mask,
                $title,
                $content,
                $confirm,
                $cancel,
                dfd,
                confirmed,
                timer;
    
            if(typeof arg=='string')
                conf.title=arg;
            else {
                conf=$.extend(conf,arg);
            }
    
            dfd=$.Deferred();
    
            $box=$('<div>' +
                    '<div class="pop-title">'+conf.title+'</div>'+
                    '<div class="pop-content">' +
                        '<div><button style="margin-right: 5px" class="primary confirm">确定</button>' +
                        '<button class="cancel">取消</button></div>'+
                    '</div>'+
                '</div>')
                .css({
                    color:'#444',
                    240,
                    height:'auto',
                    padding:'15px 10px',
                    background:'#fff',
                    position:'fixed',
                    'border-radius':3,
                    'box-shadow':'0 1px 2px rgba(0,0,0,.5)'
                })
    
            $title=$box.find('.pop-title').css({
                padding:'5px 10px',
                'font-weight':900,
                'font-size':20,
                'text-align':'center'
            })
    
            $content=$box.find('.pop-content').css({
                padding:'5px 10px',
                /*'font-weight':900,*/
                'text-align':'center'
            })
    
            $confirm=$content.find('button.confirm');
            $cancel=$content.find('button.cancel');
    
            $mask=$('<div></div>')
                .css({
                    position:'fixed',
                    background:'rgba(0,0,0,.5)',
                    top:0,
                    bottom:0,
                    right:0,
                    left:0
                });
    
            timer=setInterval(function () {
                if(confirmed!==undefined){
                    dfd.resolve(confirmed);//resolve(value),告诉对象执行done回调,value是参数(这里confirmed作为参数传给r)
                    clearInterval(timer);
                    dismiss_pop();
                }
            },50);
    
            $confirm.on('click',function () {
                confirmed=true;
                /*console.log('confirmed',confirmed);*/
            });
    
            $cancel.on('click',on_cancel);
            $mask.on('click',on_cancel);
    
            function on_cancel() {
                    confirmed=false;
            }
            function dismiss_pop() {
                $mask.remove();
                $box.remove();
            }
            
            function adjust_box_position() {
                var window_width=$window.width(),
                    window_height=$window.height(),
                    box_width=$box.width(),
                    box_height=$box.height(),
                    move_x,
                    move_y;
                move_x=(window_width-box_width)/2;
                move_y=((window_height-box_height)/2)-20;
                $box.css({
                    left:move_x,
                    top:move_y
                })
            }
            
            $window.on('resize',function () {
                adjust_box_position();
            })
    
            $mask.appendTo($body);
            $box.appendTo($body);
            $window.resize();
            return dfd.promise();
    
        }
    function listen_task_delete() {
            $delete_task_trigger.on('click',function () {
                var $this=$(this);
                /*找到删除按钮所在的元素*/
                var $item=$this.parent().parent();
                var index=$item.data('index');/*调用data-index属性*/
                //如果用confirm会将后台全部暂停
                var tmp = pop('确定删除?').then(function (r) {
                    r?delete_task(index):null;
                })
    
            })
        }

    接下来我们关注下,为什么要用deferred.promise() 函数返回 Deferred(延迟)的 Promise 对象。

    首先看这样一段代码

    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("我是来捣乱的....");

    很明显破坏了我们模拟延迟的目的.(我们的目的:将done里面的函数延迟

     将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);
    });

    如果我们的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);
    });

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

    好文推荐:http://www.cnblogs.com/guoyansi19900907/p/5000267.html

  • 相关阅读:
    理解js中的原型链,prototype与__proto__的关系
    Zepto源码(2016)——Zepto模块(核心模块)
    MySQL增删改查
    ACM典型试题--古代密码(二)
    ACM典型试题--简单的加密算法(一)
    MySQL图文安装配置
    (c语言)二叉树中序线索(数据结构十七)
    (C语言)二叉树层次遍历(数据结构十六)
    Java连接db2数据库(常用数据库连接五)
    java连接oracle数据库(常用数据库连接四)
  • 原文地址:https://www.cnblogs.com/yujihang/p/6875263.html
Copyright © 2020-2023  润新知