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特性

  

时间: 2024-10-23 12:08:20

Deferred的相关文章

twisted 6 大话deferred

http://twistedsphinx.funsize.net/projects/core/howto/deferredindepth.html 1. 没有等待,就没有事件.设备的"监听"功能是一切事件的来源. 一句话道明deferred的精髓 Normally, a function would create a deferred and hand it back to you when you request an operation that needs to wait for

deferred对象和promise对象(一)

个人认为阮一峰老师讲的关于deferred对象是最容易理解的. deferred对象是jquery的回调函数解决方案.解决了如何处理耗时操作的问题,对那些操作提供了更好的控制,以及统一的编程接口. deferred对象的功能: 1.将ajax操作改为链式 对于$.ajax()操作完成之后,如果使用的是低于1.5版本的jquery,返回的是XHR对象,高于1.5版本返回的是deferred对象.deferred对象可进行链式操作. $.ajax({ url:"test.html", su

jQuery:deferred [转]

jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本. 每个版本都会引入一些新功能.今天我想介绍的,就是从jQuery 1.5.0版本开始引入的一个新功能----deferred对象. 这个功能很重要,未来将成为jQuery的核心方法,它彻底改变了如何在jQuery中使用ajax.为了实现它,jQuery的全部ajax代码都被改写了.但是,它比较抽象,初学者很难掌握,网上的教程也不多.所以,我把自己的学习笔记整理出来了,希望对大家有用. 本文不是初级教程,针对的读者是那些已经具备

jquery的deferred使用详解

1.什么是deferred对象deferred对象是一个延迟对象,意思是函数延迟到某个点才开始执行,改变执行状态的方法有两个(成功:resolve和失败:reject),分别对应两种执行回调(成功回调函数:done和失败回调函数fail) 2.deferred对象使用示例ajax方式其实是deferred对象1)普通的ajax操作 $.ajax({ url: "index.html", success: function () { alert("success");

jquery Deferred

jquery Deferred使用经验 这周做了个小活动(http://aoqi.100bt.com/zt-2016duanzi/index.html),刚开始时候没看好需求,逻辑都写一块了 最后各种坑要填补,从中也获取了些经验和教训,下面说说这里会用到的$.Deferred: 关于jquery里面的deferred的基本使用方法,阮一峰大婶已经有文章说明了,链接如下: http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_o

延期(deferred)的承诺(promise) —— jq异步编程浅析

引子 相信各位developers对js中的异步概念不会陌生,异步操作后的逻辑由回调函数来执行,回调函数(callback function)顾名思义就是“回头调用的函数”,函数体事先已定义好,在未来的某个时候由某个事件触发调用,而这个时机,是程序本身无法控制的. 举几个常见例子: 事件绑定 动画 Ajax 上面的例子简单.典型,易于阅读和理解. 为了引出本文的主题,假设现在有3个ajax异步操作,分别为A.B.C,每个都封装成了函数,并可传入success回调作为参数. 请考虑以下场景: 希望

jQuery中的$.Deferred、$.when异步操作

前言 网页中常常会出现一些耗时比较长的操作,如ajax请求服务器数据,这些操作都不能立即得到结果.如果我们需要在这些操作执行完后来进行另外的操作,我们就需要将这些操作放在回调函数中,$.Deferred就是jQuery用来处理回调操作的.jQuery中的$.Deferred对$.Callbacks很有依赖,看看$.Callbacks执行回调. 1 var callbacks = $.Callbacks(); 2 setTimeout(function(){ 3 console.log(1); /

jQuery的Deferred

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>deferred对象</title> </head> <body> 两个对象,一个是promise对象,另一个是deferred对象. Deferred方法,返回deferred对象. Deferred里面有then方法,返回promise对象. jQuery源码 1.对参

jquery的Deferred 对象初体验

之前阅读了阮一峰老师的jQuery的deferred对象详解一文,结合jquery手册,算是对Deferred对象有了初步的认知.今天便来分享一下我自己的一些体会. 一.deferred可以方便的添加回调 先来看下面的例子 1 var test = function(callback) { 2 setTimeout(function() { 3 console.log('我完成了'); 4 callback('我是回调') 5 }, 1000) 6 }; 7 test(function(text

【转载】学习资料存档:jQuery的deferred对象详解

我在以前的文章里提到promise和deferred,这两个东西其实是对回调函数的一种写法,javascript的难点之一是回调函数,但是我们要写出优秀的javascript代码又不得不灵活运用回调函数,大型javascript代码里都会大量运用回调函数,大量的标准回调函数写法的坏处就是使得代码的阅读性和可维护性降低,因此出现了promise模式和大量deferred库,jQuery很优秀,但是jQuery早期的版本对优雅回调写法的支持远远不够,直到jQuery1.5引入了deferred后,这