jquery中的工具方法--Deferred和when

原创 2016年06月06日 14:41:08

在jquery1.5版本以后,Deferred和promise就是jquery中非常常用的两个功能,并且在这个版本利用Deferred又完全重写了ajax模块。Deferred对象是有jquery.Deferred构造的,$.Deferred在jquery代码中,分别在promise方法、Dom ready、ajax模块、动画模块被使用

Deferred的用法

var defer = $.Deferred();
function aaa(){
    console.log("aaa");
    defer.resolve();//完成态 相当于callbacks中的fire
}
aaa();
//相当于callbacks中的add方法 通过done改变状态
defer.done(function(){
    console.log('success');
});

var defer = $.Deferred();
function runAsync(){
    setTimeout(function(){
        console.log('执行完成');
        defer.resolve('data');
    },2000)
    return defer.promise();
}
defer.done(function(data){
    console.log(data);
})
defer.fail(function(data){
    console.log(data);
})
var d = runAsync();
    d.done(function(data){
    console.log(data);
})
d.then(function(data){
    console.log(data);
})

这里需要说明一下deferred和promise的区别,deferred相当于promise的继承类,相比于promise方法,deferred可以通过resolve、reject和 notify改变回调的状态,而promise不能修改回调的状态

$.Deferred的实现

异步队列的方法

添加回调函数方法

  • deferred.done(doneCallbacks[,doneCallbacks]):添加成功回调函数,当异步队列处于成功状态时被调用
  • deferred.fail(failCallbcks[,failCallbacks]): 添加失败回调函数,当异步队列处于失败状态时被触发
  • deferred.progress(progressCallbacks):添加消息回调函数
  • deferred.then(doneCallbacks,failCallbacks[,progressCallbacks]): 添加成功回调函数、失败回调函数、消息回调函数
  • deferred.always(alwaysCallbacks[,alwaysCallbacks]):添加回调函数,当异步队列处于成功或失败状态时调用

执行回调函数方法

  • deferred.resolve(args): 使用指定的参数调用所有的成功回调函数
  • deferred.resolveWith(context[,args]): 使用指定的上下文和参数调用所有的成功回调函数,异步队列进入成功状态
  • deferred.reject(args): 使用指定的参数调用所有失败回调函数,异步队列进入失败状态
  • deferred.rejectWith(context[.args]: 使用指定的上下文和参数调用所有失败回调函数,异步对垒进入失败状态
  • deferred.notify(args): 使用指定的参数调用所有的消息回调函数
  • deferred.notifyWith(context[,args]):使用指定的上下文和参数调用所有的消息回调函数

判断当前状态

  • deferred.state():判断异步队列的当前状态

工具

  • deferred.pipe([doneFilter] [,failFilter] [,progressFilter]): 返回一个新的异步队列的只读副本,通过过滤函数过滤当期那异步队列的状态和值
  • deferred.promise([target]):返回当前Deferred对象的制度副本,或者为普通对象增加异步队列的功能

实现过程

方法jQuery.Deferred(func)创建异步队列的5个关键步骤如下

  1. 创建三个$.Callbacks对象,分别表示函数执行的三个状态,即未完成、完成和失败,并设置初始状态为待定(pending)
  2. 创建异步队列的只读副本promise,其中包含了方法done() 、fail()、progress()、state()、isRejected()、then()、always()、pipe()、promise()
  3. 定义异步队列deferred

    a. 把只读副本promise中的方法添加到异步队列deferred中
    b. 为异步队列deferred添加触发执行成功、失败、消息回调函数的方法,包括resolve()、resolveWith()、reject()、rejectWith()、notify()、notifyWith()
    c. 为异步队列deferred添加设置状态的回调函数

  4. 如果传入函数参数func,则调用。

  5. 返回异步队列deferred

    // 扩展了工具方法Deferred和when
    // $.Deferred();
    // $.when()
    // 延迟对象是基于callbacks对象实现的
    // 实现了对异步的的统一管理
    
    // 提供了always then promise pipe when 等实用的方法 
    jQuery.extend({
    
    Deferred: function( func ) {
        var tuples = [
            // action, add listener, listener list, final state
            // 分别对应完成态 失败态和未完成态
            // resolve相当于callbacks对象中的fire方法 done相当于add方法  resolved为状态
            [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
            [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
            [ "notify", "progress", jQuery.Callbacks("memory") ]
        ],
        state = "pending",
        promise = {
            // 该方法返回当前的状态
            state: function() {
                return state;
            },
            //完成态和失败态都执行该操作
            always: function() {
                deferred.done( arguments ).fail( arguments );
                return this;
            },
            //then方法同时绑定完成态、失败态和未完成态三个回调函数
            then: function( /* fnDone, fnFail, fnProgress */ ) {
                var fns = arguments;
                return jQuery.Deferred(function( newDefer ) {
                    jQuery.each( tuples, function( i, tuple ) {
                        var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
                        // deferred[ done | fail | progress ] for forwarding actions to newDefer
                        // 对各个状态添加回调函数
                        deferred[ tuple[1] ](function() {
                            var returned = fn && fn.apply( this, arguments );
                            //确定回调函数的返回值 
                            if ( returned && jQuery.isFunction( returned.promise ) ) {
                                returned.promise()
                                    .done( newDefer.resolve )
                                    .fail( newDefer.reject )
                                    .progress( newDefer.notify );
                            } else {
                                newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                            }
                        });
                    });
                    fns = null;
                }).promise();
            },
            // Get a promise for this deferred
            // If obj is provided, the promise aspect is added to the object
            promise: function( obj ) {
                return obj != null ? jQuery.extend( obj, promise ) : promise;
            }
        },
        deferred = {};
    
    // Keep pipe for back-compat
    promise.pipe = promise.then;
    
    // Add list-specific methods
    // 对映射数组进行遍历操作
    jQuery.each( tuples, function( i, tuple ) {
        var list = tuple[ 2 ],//callback回调
            stateString = tuple[ 3 ];//状态字符串
    
        // promise[ done | fail | progress ] = list.add
        // 对done、fail和progress等同于list.add方法 并将添加回调的方法添加至promise对象中
        promise[ tuple[1] ] = list.add;
    
        // Handle state
        // 只有完成态和失败态有statestring
        if ( stateString ) {
            list.add(function() {
                // state = [ resolved | rejected ]
                state = stateString;
    
            //当完成态的回调被调用时 失败态和进行态就不能被调用
            //i^1为位运算符 当i为1时 结果为0,当i为0时 结果为1
            // [ reject_list | resolve_list ].disable; progress_list.lock
            }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
        }
    
        // deferred[ resolve | reject | notify ]
        deferred[ tuple[0] ] = function() {
            deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
            return this;
        };
        deferred[ tuple[0] + "With" ] = list.fireWith;
    });
    
    // Make the deferred a promise
    // 将promise和deferred合并
    promise.promise( deferred );
    
    // Call given func if any
    if ( func ) {
        func.call( deferred, deferred );
    }
    
    // All done!
    return deferred;
    },
    

$.when()的用法


$.when方法实现多个回调函数的控制

function aaa(){
    var defer = $.Deferred();
    defer.resolve();
    return defer;
}

function bbb(){
    var defer = $.Deferred();
    defer.resolve();

    return defer;
}
//需求:当两个回调都成功的时候执行操作
//when主要是针对多延迟对象的操作
$.when(aaa(),bbb()).done(function(){
    alert('成功');
}).fail(function(){
    alert('失败');
})

$.when方法中,只有参数中的所有函数都为resolve时,才执行done操作,只要有一个为reject,立即执行fail操作

$.when的实现

  1. 接收若干个对象,参数仅一个且返回值不是Deferred对象将立即执行回调函数
  2. 返回值为Deferred对象和非Deferred对象混杂时,对于非Deferred对象的计数器减一
  3. 当所传参数中所有的Deferred对象都resolve后,执行回调
    // Deferred helper
    when: function( subordinate /* , …, subordinateN */ ) {
    var i = 0,
    resolveValues = slice.call( arguments ),//将arguments变为数组
    length = resolveValues.length,

            // the count of uncompleted subordinates
            // 记录未完成态的回调函数的个数 
            // 当传入的参数为0或传入的参数的返回值不是延迟对象的时候 length为0;
            remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
    
            // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
            // 若只有一个参数,延迟对象即为传入的参数
            // 若没有参数或参数大于1 创建新的延迟对象
            deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
    
        // Update function for both resolve and progress values
        // 更新计数器 
        updateFunc = function( i, contexts, values ) {
            return function( value ) {
                contexts[ i ] = this;
                values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
                if ( values === progressValues ) {
                    deferred.notifyWith( contexts, values );
                    //当计数器减为0时 执行resolve
                } else if ( !(--remaining) ) {
                    deferred.resolveWith( contexts, values );
                }
            };
        },
    
        progressValues, progressContexts, resolveContexts;
    
    // add listeners to Deferred subordinates; treat others as resolved
    if ( length > 1 ) {
        progressValues = new Array( length );
        progressContexts = new Array( length );
        resolveContexts = new Array( length );
        for ( ; i < length; i++ ) {
            if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
                resolveValues[ i ].promise()
                    .done( updateFunc( i, resolveContexts, resolveValues ) )
                    .fail( deferred.reject )//只要有一个失败 触发reject
                    .progress( updateFunc( i, progressContexts, progressValues ) );
            } else {
                //当参数的返回值不是延迟对象 对remaining减1
                --remaining;
            }
        }
    }
    
    // if we're not waiting on anything, resolve the master
    // 针对没有参数或都是完成态的情况
    if ( !remaining ) {
        deferred.resolveWith( resolveContexts, resolveValues );
    }
    
    return deferred.promise();
    }
    });
    
版权声明:本文为博主原创文章,未经博主允许不得转载。

JQuery when

jQuery.when(deferreds) 参数deferreds,一个或多个延时对象或JS对象,我们初略的认为它就是一个或多个异步请求。 例如: $.when($.ajax("page1...
  • StarRexStar
  • StarRexStar
  • 2012年09月04日 10:52
  • 9149

jquery 实现原理四:deferred对象

一,deferred的使用和基本原理 具体的用法可以参见阮一峰的一篇日志:http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_o...
  • lihongxun945
  • lihongxun945
  • 2013年09月11日 21:48
  • 5200

jquery的deferred使用详解

前言之前在一篇文章中写过一点when方法的使用,最近看到同事的一篇文章,又详细学习了deferred方法的各种使用技巧。特此总结。简单介绍deferred对象就是jQuery的回调函数解决方案,jQu...
  • mevicky
  • mevicky
  • 2016年04月27日 11:12
  • 4125

jQuery.Deferred和Promise

JS对无法及时得到结果的操作,一般都是进行异步操作,然后通过某些模式来得到结果。jQuery开始在版本1.5中提供了Deferred对象,方便异步操作。在jQuery版本1.8中,可以采用promi...
  • whereismatrix
  • whereismatrix
  • 2015年11月22日 17:29
  • 977

jquery的when和Deferred方法

jQuery(function($){ /*当两个异步方法都执行完毕后,执行回调*/ $.when(ajax1(),ajax2()).done(function(){ alert("do...
  • xiaobaoxiaodun
  • xiaobaoxiaodun
  • 2015年03月31日 15:41
  • 2871

关于$.when(),$.apply(),$.when.apply(),$.when.apply().then()的用法(项目总结)

项目中有这样的应用,以前基本上没有用到过$.when(), $.apply()这些方法,所以就自己百度了一下 $.when() jQuery.when() 方法 jQuery 杂项方法 ...
  • xiaolinlife
  • xiaolinlife
  • 2017年06月16日 10:56
  • 1647

jQuery的deferred对象使用详解

jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本。每个版本都会引入一些新功能。今天我想介绍的,就是从jQuery 1.5.0版本开始引入的一个新功能----deferred对象。 ...
  • aa1049372051
  • aa1049372051
  • 2016年06月14日 09:42
  • 5754

jquery的deferred使用详解

前言之前在一篇文章中写过一点when方法的使用,最近看到同事的一篇文章,又详细学习了deferred方法的各种使用技巧。特此总结。简单介绍deferred对象就是jQuery的回调函数解决方案,jQu...
  • mevicky
  • mevicky
  • 2016年04月27日 11:12
  • 4125

一个基于jQuery延迟对象Deferred的异步任务处理方法

介绍了一个基于jQuery延迟对象Deferred的异步处理方法:一段无css的js脚本代码。...
  • hulihui
  • hulihui
  • 2016年12月18日 09:42
  • 1159

jQuery的deferred对象使用详解——实现ajax同步请求数据

最近遇到一个ajax请求数据的问题 ,就是想要请求3个不同的接口,然后请求完毕后对数据进行操作,主要问题就是不知道这3个请求誰先返回来,或者是在进行操作的时候不能保证数据都已经回来,首先想到能完成的就...
  • ljj_9
  • ljj_9
  • 2016年10月12日 14:56
  • 870
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:jquery中的工具方法--Deferred和when
举报原因:
原因补充:

(最多只允许输入30个字)