首页 > 代码库 > Deferred

Deferred

最近在看deferred,有一些疑问,于是研究了一下,现在理解程度也就是70%-80%

这个方法基于callbacks,所以说,如果要看原理,最好先看一下callbacks的源码。下面我贴上源码,进行分析一下,会加上自己的理解,我主要困惑在when和then方法上,会重点说一下,这里说一下基本的源码分析网上有很多,大家可以搜一下,这里链接一个when和then方法的例子地址:

http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html

http://www.css88.com/jqapi-1.9/deferred.then/

ok,进入源码jQuery.extend({

    Deferred: function( func ) {
    //开始定义变量
var tuples = [ // action, add listener, listener list, final state [ "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:
function( /* fnDone, fnFail, fnProgress */ ) {
            //保存参数
var fns = arguments;
            //直接返回一个deferred的方法,即直接返回一个deferred对象,
            //这里的newDefer就是我们的return jQuery.Deferred(。。)的返回的新的deferred对象,源码Deferred方法的参数如果是函数的话
            //会立即执行,源码如下:
if ( func ) {func.call( deferred, deferred );},此时会把deferred作为参数传递进去
  
            return jQuery.Deferred(function( newDefer ) {              //循环遍历tuples,分别对不同状态注册函数                jQuery.each( tuples, function( i, tuple ) {
                //取出“动作”:resolve(解决)、reject(拒绝)、notify(通知)                            var action = tuple[ 0 ],
                //取出对应回调函数,这里的i用的比较巧妙,如果fns[i]不存在返回false,否则返回函数 fn
= jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer
                //这里dererred的各个状态的add方法添加了newDefer的fire方法(else中体现了),以后deferred若果fire的话,这个新的newDefer也同样fire
deferred[ tuple[1] ](function() {
                   //这里注意一下,这里相当于if(fn){returned = fn.apply(this,arguments)};
                   //这时候的returned相当于函数fn执行完毕之后的结果,后面举例子,就会看到效果了,这个this和arguments就是在fire的时候传递进来的,源码如下(for each中体现的):
                  //deferred[ tuple[0] ] = function() {deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );return this;};
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 {
                     //直接调用各个状态的fireWith方法,如果fn存在就直接把returned放入 newDefer[ action
+ "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扩展obj,即把promise的属性都复制到到obj中,如果obj不存在则直接返回promise
         //promise跟deferred区别在于deferred有很多可以改变对象的方法(rejectWith、resolveWith等),promise没有 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
    //这里主要就是把tuples的各个属性进行一一解析赋值到defferred中,
jQuery.each( tuples, function( i, tuple ) {
       //list就是各个状态的callbacks的list,stateString是状态
var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add
//把各个状态的done、fail、progress方法直接挂到callbacks的add上面
promise[ tuple[1] ] = list.add; // Handle state
   //如果状态存在,也就是tuples的前两组
if ( stateString ) {
          //done和fail状态,分别添加三个函数(改变自己的state,把另外一个状态设置为不可用,把第三个状态锁死),这样为了保证只有一个状态执行例如:done的时候fail和process就不能执行了 list.add(
function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock
          //这个学一下,i^1,这个是异或运算符,效率高,特点:只有一个是1的时候才等于1
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ]
//定义fireWith方法,所谓的修改方法
deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; };
       //把fireWith跟callbacks的fireWith进行绑定 deferred[ tuple[
0] + "With" ] = list.fireWith; }); // Make the deferred a promise
//扩展deferred,把promise的属性扩展到defrred中
promise.promise( deferred ); // Call given func if any
//刚才上面提到的,Deferred()的参数如果是函数,则立即调用,参数就是将要返回的deferred对象
if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper
//这个方法是jq的扩展,他的作用就是把放入其中的N个异步方法做一个计数,知道最后一个方法完成之后,才执行when后面的方法
when: function( subordinate /* , ..., subordinateN */ ) {
     //定义变量,resolveValues把参数转化成数组
var i = 0, resolveValues = core_slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates
//remaining存放参数长度,也就是通过它来通知when是否所有的异步方法都执行完了
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
     //如果remaining等于1,则肯定是deferred,如果参数大于1,则新定义一个deferred
deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values
       //更新remaining长度,以及判断是否是最后一个异步,如果是最后一个,则调用deferred的fire方法,从而触发when()后面的方法调用
updateFunc = function( i, contexts, values ) { return function( value ) { contexts[ i ] = this; values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; if( values === progressValues ) { deferred.notifyWith( contexts, values ); } else if ( !( --remaining ) ) {
              //计数为0的时候,调用deferred的fire方法 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++ ) {
          //判断传进来的参数是否是deferred,不是则直接计数减一,是的话,调用deferred的promise()方法返回promise对象,然后添加updateFunc函数,
          //当参数中的函数fire的时候,就调用updateFunc函数,计数减一,直到为0
          //整体思路,就是定义一个全局变量保存总共的参数个数,然后写一个公共函数,分配到各个参数函数中,每次函数执行完毕,就执行公共函数去操作公共变量减一,
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress( updateFunc( i, progressContexts, progressValues ) ); } else { --remaining; } } } // if we‘re not waiting on anything, resolve the master if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); }});

 when函数例子:

var dtd = $.Deferred(); // 新建一个deferred对象
  var wait = function(dtd){
    var tasks = function(){
      alert("执行完毕!");
      dtd.resolve(); // 改变deferred对象的执行状态
    };
    setTimeout(tasks,5000);
    return dtd;
  };
$.when(wait(dtd))
  .done(function(){ alert("哈哈,成功了!"); })
  .fail(function(){ alert("出错啦!"); });

 

then函数例子:

var filterResolve = function() {
  var defer = $.Deferred(),
  filtered = defer.then(function( value ) {
    return value * 2;
  });

  defer.resolve( 5 );
  filtered.done(function( value ) {
    alert(value);//10
  });
};

filterResolve();

说说这个then的执行逻辑,在源代码中也提到过

 1)定义一个deferred对象defer

 2)defer的then方法执行过程,可以这么理解

  a)var filtered = $.Deferred(),fn = function(value){return value*2;}

  b)defer.add(filtered.resolve(fn()));

当调用defer的add的时候,相当于filtered的fire方法,而fire的参数是fn()函数的返回值,

当filtered再次done(add方法)的时候,直接执行上次的fire方法,而参数也是上一个的参数,参见callbacks的 once memory特性

  

 

Deferred