zoukankan      html  css  js  c++  java
  • jquery源码解析:jQuery工具方法when详解

    我们先来看when方法是如何使用的:

    var cb = $.when();   //when方法也是返回一个延迟对象,源码是return deferred.promise();返回的延迟对象不能修改状态

    $.Deferred()也是返回一个延迟对象,那么它们的区别是什么呢?$.Deferred()只能针对一个延迟对象做成功,失败,进行中的操作,而$.when()可以针对多个延迟对象做以上操作。举个例子:

    function a(){

      var cb = $.Deferred();

      cb.resolve();

      return cb;

    }

    function b(){

      var cb = $.Deferred();

      cb.resolve();

      return cb;

    }

    a().done(function(){alert("成功")});

    以上代码,我的需求是等a中的延迟对象触发,以及b中的延迟对象触发,后,再弹出成功。因为这时需要处理两个延迟对象,所以通过 $.Deferred()无法实现。这时就需要用$.when()了。我们把最后面那句代码改成:

    $.when(a(),b()).done(function(){alert("成功")});就可以实现上面的需求了。这句代码的意思就是,等a,b中的延迟对象都触发了resolve后,才会执行里面回调方法,弹出成功。

    还有一个用法:

    $.when(a(),b()).done(function(){alert("成功")}).fail(function(){alert("失败")}),这段代码的意思,只要a或b中的任何一个延迟对象触发reject方法,就会弹出失败。

    大家看到when方法里面传的都是延迟对象,而且这个延迟都是具有状态的(调用resolve是成功状态,调用reject是失败状态)。如果传入的不是延迟对象,比如:$.when("chaojidan",b()).done(function(){alert("成功")}).fail(function(){alert("失败")}),那么when方法会忽略chaojidan这个字符串,只会针对b里面的延迟对象进行处理。如果都是字符串,那么将都忽略,直接执行done添加的方法。而且done添加的回调方法中,可以接收到这些字符串(arguments[0]是第一个字符串,arguments[1]是第二字符串,以此类推)。

    接下来进行源码解析:

    when: function( subordinate , ...  , subordinateN ) {
      var i = 0,
        resolveValues = core_slice.call( arguments ),
          length = resolveValues.length,

            remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,

              deferred = remaining === 1 ? subordinate : jQuery.Deferred(),  //如果传入一个延迟对象,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 ) ) {  //如果触发的是resolve,remaining就会减1,直到为0,就证明when里面的所有延迟对象都执行了resolve,这时,就会执行when返回的延迟对象deferred的resolveWith,而这将会触发deferred通过done添加的方法。
                      deferred.resolveWith( contexts, values );
                    }
                  };
                },

                  progressValues, progressContexts, resolveContexts;

      if ( length > 1 ) {   //传入2个以及2个以上的参数时,进入if语句
        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 ) )   //如果一个延迟对象触发resolve,就执行updateFunc方法返回的函数,而这个函数会判断所有的延迟对象都触发resolve了没有,如果都触发了,那么就执行when返回的延迟对象的resolve。如果不是全部,那么不做处理
                .fail( deferred.reject )   //只要有一个延迟对象触发reject,就会执行when返回的延迟对象的reject。
                  .progress( updateFunc( i, progressContexts, progressValues ) );
          } else {    //传入的如果不是延迟对象,就直接减一。
            --remaining;
          }
        }
      }

      if ( !remaining ) {    //当不传参数的时候(或传一个字符串的时候),直接执行这里
        deferred.resolveWith( resolveContexts, resolveValues );
      }

      return deferred.promise();
    }

     when方法最难的莫过于updateFunc方法,这个方法会返回一个函数,而这个函数会调用updateFunc方法中的参数,形成一个闭包。我们来分析第一个updateFunc方法,updateFunc( i, resolveContexts, resolveValues ),这里假设传进when方法的是a,b两个延迟对象。当第一次循环时,done中的updateFunc方法,i=0,resolveContexts = [undefined , undefined ],resolveValues =[a,b],执行updateFunc方法返回一个function。progress中的updateFunc方法,i=0,progressContexts = [undefined , undefined ],progressValues =[undefined , undefined ],执行updateFunc方法返回一个function。第二次循环时,只是i=1,其他都是一样的。之后,我们触发a的resolve方法,这时就会执行i=0的这个function。

    function( value ) {    //如果resolve没有传参数,那么value为undefined。其中函数中的contexts=resolveContexts,values = resolveValues 
          contexts[ i ] = this;    
          values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;   //如果没有传入参数,那么arguments.length=0,resolveValues[0] = undefined。resolveValues=[undefined , b],延迟对象a就变成undefined
          if( values === progressValues ) {   //[undefined , b]不是[undefined , undefined ],就把remaining减一,刚开始remaining是2,因为传入了两个延迟对象a,b。
                deferred.notifyWith( contexts, values );
          } else if ( !( --remaining ) ) {        //减1后,还是1,所以不执行下面的方法。
                deferred.resolveWith( contexts, values );
          }
    }

    当b执行resolve方法时,也会执行上面的function,这时i=1,所以resolveValues[1] =undefined,resolveValues=[a, undefined ],它不等于[undefined , undefined ],所以执行--remaining,这时remaining变成0了,就会执行deferred.resolveWith( contexts, values );方法了。因为这时a,b两个延迟对象都执行了resolve了。

    上面讲的都是执行延迟对象a,b的resolve方法。我们再来讲一下执行a,b的notify方法。

    我们首先执行a的notify方法,这时会执行i=0的function

    function( value ) {    //如果notify没有传参数,那么value为undefined。其中函数中的contexts=progressContexts ,values = progressValues 
          contexts[ i ] = this;    
          values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;   //如果没有传入参数,那么arguments.length=0,progressValues[0] = undefined。progressValues =[undefined , undefined]
          if( values === progressValues ) {   //[undefined , undefined ]还是[undefined , undefined ],就进入if语句,触发when返回的延迟对象deferred的notify方法,当然会触发deferred的progress添加的函数。
                deferred.notifyWith( contexts, values );
          } else if ( !( --remaining ) ) {        
                deferred.resolveWith( contexts, values );
          }
    }

    也就是不传参(value等于undefined)的情况下,触发其中一个(a或b)notify,也会触发when返回的延迟对象的notify。

    那如果传参的话,function执行的流程就不是上面的流程了,传参还分一个还是多个这两种情况。按照我上面的执行流程,很容易理解。说实话,没必要,知道大概的执行流程就行了,最重要的知道怎么用就行了。

    加油!

  • 相关阅读:
    如何搭建PHP本地服务器
    load xml with xls
    t-sql read xlsx
    test js online link
    export to pdf
    silverlight browse information
    ckeditor link
    T-Sql操作Xml数据(转)
    Linq to Xml
    webpack -p压缩打包react报语法错误处理
  • 原文地址:https://www.cnblogs.com/chaojidan/p/4170697.html
Copyright © 2011-2022 走看看