• jquery源码解析:jQuery延迟对象Deferred(工具方法)详解2


    请接着上一课继续看。

    $.Deferred()方法中,有两个对象,一个是deferred对象,一个是promise对象。

    promise对象有以下几个方法:state,always,then,promise,pipe,done,fail,progress。

    deferred对象除了有promise对象的所有方法外(通过jQuery.extend( obj, promise ),把promise对象的所有方法复制到deferred对象中),还有其他三个:resolve,reject,notify。

    他们之间的区别是什么?我们先来看一个例子;

    function a(){

      var cb = $.Deferred();

      setTimeout(function(){

        cb.resolve();

      },1000)

      return cb;

    }

    var newCB = a();

    newCB.done(function(){

      alert("成功");

    }).fail(function(){

      alert("失败")

    });

    以上代码,在一秒后,会弹出成功。但是如果我们在代码的最后再加上newCB.reject();只会弹出失败,成功不会弹出了(因为先执行reject方法,而延迟对象的实现是once的,所以resolve调用后,不会执行弹出成功的方法)。以上我们就可以知道,我们可以在代码中改变延迟对象的状态(本来是弹出成功,但是弹出了失败)。

    解决办法:我们在a方法中,return时,我们return cb.promise()。这时,延迟对象cb就不会改变状态了。你在代码最后面加上cb.reject会报错(因为promise对象没有resolve,reject,notify方法,只有deferred对象有)。

    从上可知,deferred对象可以改变状态,promise对象不能改变状态。

    cb.promise()方法源码是:

    promise: function( obj ) {
      return obj != null ? jQuery.extend( obj, promise ) : promise;
    }

    返回的就是promise对象。

    接下来,我们来看下延迟对象的状态是如何变化的:

    延迟对象总共有三个状态:默认为pending。成功为resolved,失败为rejected。

    源代码里面是这样改变的:

    var tuples = [
      [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
      [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
      [ "notify", "progress", jQuery.Callbacks("memory") ]
    ],

    state = "pending",

    jQuery.each( tuples, function( i, tuple ) {
      var list = tuple[ 2 ],
      stateString = tuple[ 3 ];    //resolved,rejected

      promise[ tuple[1] ] = list.add;  //经过下面的add添加,promise = {"done":[functon(){},fail.disable,progress.lock],"fail":[functon(){},done.disable,progress.lock]}

      if ( stateString ) {      //进入到if语句,能进入到if语句的就是resolved状态和rejected状态
        list.add(function() {  
              state = stateString;

              tuples[0][2] = jQuery.Callbacks("once memory")

          }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );    // [ reject_list | resolve_list ].disable; progress_list.lock

           //往list中添加三个方法。第一个方法就是改变状态。因为成功状态和失败状态只能出现一个,并且只有一次,因此后面的两个方法是来实现不能进行状态的改变。其中tuples[ 2 ][ 2 ].lock,其实就是jQuery.Callbacks("memory").lock,意思就是进行中的后面的fire方法无效,不能再触发fire了。而第二个方法disable是禁止回调的所有功能。i=0或1,当 i=0时,代表成功的状态,tuples[1][2]= jQuery.Callbacks("once memory"),这时代表失败的状态(fail)的所有功能都不能用。当i=1时,代表失败的状态,那么tuples[0][2]代表成功的状态(done)的所有功能都不能用。意思就是,当执行了resolve时,它代表成功状态,这时reject(状态会变成失败)不会再触发了。

        //更简单的理解是:当resolve调用后,状态变成resolved,然后失败状态的reject,fail有关的任何方法都不能执行了,而且进行中状态的fire方法(notify方法)也不能执行了但是它的progress添加的方法,还是可以执行的(如果notfy方法在resolve方法之前调用)。
      }

      .....

    }

    always方法也是用来添加方法,比如:cb.always(function(){}),但是,这个方法的意思是,不管是成功还是失败,它里面的方法都会执行。

    always: function() {
        deferred.done( arguments ).fail( arguments );
        return this;
    },

    then方法:cb.then(function(){成功},function(){失败},function(){进行中});可以同时添加成功,失败,进行中三个回调方法。

    then: function( fnDone, fnFail, fnProgress) {
        var fns = arguments;
        return jQuery.Deferred(function( newDefer ) {    //返回一个无法改变状态的延迟对象promise,在回调方法中,传入newDefer是一个新的延迟对象deferred
          jQuery.each( tuples, function( i, tuple ) {   //第一次循环时,i=0,tuple=[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ]
            var action = tuple[ 0 ],     //resolve
            fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
            deferred[ tuple[1] ](function() {    //调用老的延迟对象deferred的done方法,也就是deferred.done(function(){}),所以老的deferred调用resolve时,会执行这个回调方法,而这个回调方法,会执行新的延迟对象newDefer中通过done添加的回调方法
              var returned = fn && fn.apply( this, arguments );    //执行then(pipe)添加的方法,这里是function(){成功}
              if ( returned && jQuery.isFunction( returned.promise ) ) {   //如果返回值是一个延迟对象就进入if语句
                returned.promise()    //如果返回的是一个延迟对象returned,就得到它的promise,然后就用一个hash跟新的延迟对象进行映射。promise的resolve一调用,就会执行newDefer的resolve,就会执行newDefer通过done添加的方法。
                  .done( newDefer.resolve )
                    .fail( newDefer.reject )
                      .progress( newDefer.notify );
              }

              else {      //如果返回的是字符串,newDefer[resolveWith](执行上下文, [返回值]),而延迟对象的resolveWith方法就是list.fireWith方法,就会执行newDefer.done添加的方法fn。并把参数值(returned字符串)传进这个回调方法fn。
                newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
                }
            });
          });
          fns = null;
        }).promise();
    },

    promise.pipe = promise.then;    //pipe方法就是then方法。

    延迟对象的pipe方法的使用:

    var cb = $.Deferred();

    setTimeout(function(){

      cb.resolve("hi ");   //本来这里是触发cb.done添加的方法,但是触发了newCB.done添加的方法

    },1000)

    var newCB = cb.pipe(function(){    //pipe方法的意思是管道,意思是可以扩展延迟对象。这里就扩展了cb使之第一次参数后面有chaojidan字符串.因此扩展出来的新的延迟对象newCB就会在第一个参数后面加上chaojidan

      return arguments[0] + "chaojidan";

    })

     newCB.done(function(){    

      alert(arguments[0]);    //这里会弹出hi chaojidan。因为newCB是cb扩展出来的,也就是继承出来的,所以cb.resolve会触发newCB.done添加的方法。

    })

    以上此方法then源码比较绕,对前端开发没什么帮助,就是一些逻辑上的处理,可以忽略掉。而且这个方法使用的也比较少,知道怎么使用,知道方法是用来干嘛的就行了。

    加油!

  • 相关阅读:
    Oracle 表空间查询
    FlaskAppBuilder 中文文档 markdown格式
    Springboot + Stomp + React 实现通过前后端通信
    Jmeter自动生成UUID
    Jmeter响应断言中,中文匹配失败问题解决
    IOS使用纯代码布局替换掉默认的storyboard
    MySQL在Windows上创建服务和删除服务
    IOS15上纯代码布局之导航控制器的导航条为透明的问题
    PHP编译FTP扩展
    ansible处理一些逻辑请求思路
  • 原文地址:https://www.cnblogs.com/chaojidan/p/4168382.html
Copyright © 2020-2023  润新知