zoukankan      html  css  js  c++  java
  • zepto源码研究

    简要:zepto的deferred.js 并不遵守promise/A+ 规范,而在jquery v3.0.0中的defer在一定程度上实现了promise/A+ ,因此本文主要研究jquery v3.0.0中的defer。

    首先   在上源码前,本人觉得有必要认识一下promise/A+ 规范:https://segmentfault.com/a/1190000002452115

    接下来上源码:

    define( [
        "./core",
        "./var/slice",
        "./callbacks"
    ], function( jQuery, slice ) {
    
    "use strict";
    
    function Identity( v ) {
        return v;
    }
    function Thrower( ex ) {
        throw ex;
    }
    
    function adoptValue( value, resolve, reject ) {
        var method;
    
        try {
    
            // Check for promise aspect first to privilege synchronous behavior
            if ( value && jQuery.isFunction( ( method = value.promise ) ) ) {
                method.call( value ).done( resolve ).fail( reject );
    
            // Other thenables
            } else if ( value && jQuery.isFunction( ( method = value.then ) ) ) {
                method.call( value, resolve, reject );
    
            // Other non-thenables
            } else {
    
                // Support: Android 4.0 only
                // Strict mode functions invoked without .call/.apply get global-object context
                // 假设value是常量,resolve会立刻调用,并且传入参数为value
                resolve.call( undefined, value );
            }
    
        // For Promises/A+, convert exceptions into rejections
        // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
        // Deferred#then to conditionally suppress rejection.
        } catch ( value ) {
    
            // Support: Android 4.0 only
            // Strict mode functions invoked without .call/.apply get global-object context
            // 这里执行master.reject(value)
            reject.call( undefined, value );
        }
    }
    
    jQuery.extend( {
    
        Deferred: function( func ) {
            //元组:描述状态、状态切换方法名、对应状态执行方法名、回调列表的关系
            //tuple引自C++/python,和list的区别是,它不可改变 ,用来存储常量集
            var tuples = [
    
                    // action, add listener, callbacks,
                    // ... .then handlers, argument index, [final state]
                    [ "notify", "progress", jQuery.Callbacks( "memory" ),
                        jQuery.Callbacks( "memory" ), 2 ],
                    [ "resolve", "done", jQuery.Callbacks( "once memory" ),
                        jQuery.Callbacks( "once memory" ), 0, "resolved" ],
                    [ "reject", "fail", jQuery.Callbacks( "once memory" ),
                        jQuery.Callbacks( "once memory" ), 1, "rejected" ]
                ],
                state = "pending",  //Promise初始状态
            //promise对象,promise和deferred的区别是:
            /*promise只包含执行阶段的方法always(),then(),done(),fail(),progress()及辅助方法state()、promise()等。
             deferred则在继承promise的基础上,增加切换状态的方法,resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith()*/
            //所以称promise是deferred的只读副本
                promise = {
                    /**
                     * 返回状态
                     * @returns {string}
                     */
                    state: function() {
                        return state;
                    },
                    /**
                     * 成功/失败状态的 回调调用
                     * @returns {*}
                     */
                    always: function() {
                        deferred.done( arguments ).fail( arguments );
                        return this;
                    },
                    // TODO 待解释
                    "catch": function( fn ) {
                        return promise.then( null, fn );
                    },
                    /**
                     *
                     * @returns promise对象
                     */
                    // Keep pipe for back-compat
                    pipe: function( /* fnDone, fnFail, fnProgress */ ) {
                        var fns = arguments;
                        //注意,这无论如何都会返回一个新的Deferred只读副本,
                        //所以正常为一个deferred添加成功,失败,千万不要用pipe,用done,fail
                        return jQuery.Deferred( function( newDefer ) {
                            jQuery.each( tuples, function( i, tuple ) {
    
                                // Map tuples (progress, done, fail) to arguments (done, fail, progress)
                                // 根据tuple,从fns里取出对应的fn
                                var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
    
                                // deferred.progress(function() { bind to newDefer or newDefer.notify })
                                // deferred.done(function() { bind to newDefer or newDefer.resolve })
                                // deferred.fail(function() { bind to newDefer or newDefer.reject })
                                //注册fn的包装函数
                                deferred[ tuple[ 1 ] ]( function() {
                                    //直接执行新添加的回调 fnDone fnFailed fnProgress
                                    var returned = fn && fn.apply( this, arguments );
                                    //返回结果是promise对象
                                    if ( returned && jQuery.isFunction( returned.promise ) ) {
                                        //转向fnDone fnFailed fnProgress返回的promise对象
                                        //注意,这里是两个promise对象的数据交流
                                        //新deferrred对象切换为对应的成功/失败/通知状态,传递的参数为 returned.promise() 给予的参数值
                                        returned.promise()
                                            .progress( newDefer.notify )
                                            .done( newDefer.resolve )
                                            .fail( newDefer.reject );
                                    } else {
                                        //新deferrred对象切换为对应的成功/失败/通知状态
                                        newDefer[ tuple[ 0 ] + "With" ](
                                            this,
                                            fn ? [ returned ] : arguments
                                        );
                                    }
                                } );
                            } );
                            fns = null;
                        } ).promise();
                    },
                    then: function( onFulfilled, onRejected, onProgress ) {
                        var maxDepth = 0;
                        //depth == 0;
                        //special == newDefer.notifyWith,用来判断是否为pending状态触发
                        //deferred == jQuery.Deferred() 返回的新的defer对象即then()返回的对象即newDefer
                        //handler == [onFulfilled,onRejected,onProgress]||Identity,Identity为简单返回形参的fn,
                        //在resolve返回的fn中handle以deferred为上下文执行
                        function resolve( depth, deferred, handler, special ) {
                            //这个fn会在then的调用者对应的回调列表中,是handler的包装函数,在此称之为wrappeHandler;
    
                            return function() {
                                //这个this是不定的,比如 fn.call(obj,[args])
                                var that = this,
                                    args = arguments,
                                    //TODO 这是什么功能?
                                    mightThrow = function() {
    
                                        var returned, then;
    
                                        // Support: Promises/A+ section 2.3.3.3.3
                                        // https://promisesaplus.com/#point-59
                                        // Ignore double-resolution attempts
                                        // 一般是0和0,1和1,有种情况是0和1
                                        // defer.then(sfn,rfn,nfn);nfn先执行并返回promise,该promise会then(fn1),
                                        //    注:此fn1是由resolve(depth = 0)生成,因此fn1内部执行时depth == 0
                                        // 接下来执行sfn,此时返回promise1,maxDepth++,
                                        // 如果接下来promise被resolve了,便会执行fn1,便会出现上述情况
                                        if ( depth < maxDepth ) {
                                             return;
                                        }
                                        //传入的handler以当前上下文和参数执行
                                        returned = handler.apply( that, args );
    
                                        // Support: Promises/A+ section 2.3.1
                                        // https://promisesaplus.com/#point-48
                                        // newPromise1 = defer.then(function(){return newPromise});
                                        // newPromise在resolve时执行回调函数fn1,而fn1执行newPromise1的回调函数,
                                        // 若newPromise1 === newPromise,则会出现死循环
                                        if ( returned === deferred.promise() ) {
                                            throw new TypeError( "Thenable self-resolution" );
                                        }
    
                                        // Support: Promises/A+ sections 2.3.3.1, 3.5
                                        // https://promisesaplus.com/#point-54
                                        // https://promisesaplus.com/#point-75
                                        // Retrieve `then` only once
                                        // 如果有returned.then,则returned为promise
                                        then = returned &&
    
                                            // Support: Promises/A+ section 2.3.4
                                            // https://promisesaplus.com/#point-64
                                            // Only check objects and functions for thenability
                                            ( typeof returned === "object" ||
                                                typeof returned === "function" ) &&
                                            returned.then;
    
                                        // Handle a returned thenable
                                        if ( jQuery.isFunction( then ) ) {
    
                                            // Special processors (notify) just wait for resolution
                                            // special可判断此wrappeHandler在pending列表中还是在resolver或reject中
                                            if ( special ) {
                                                then.call(
                                                    returned,
                                                    resolve( maxDepth, deferred, Identity, special ),
                                                    resolve( maxDepth, deferred, Thrower, special )
                                                );
    
                                            // Normal processors (resolve) also hook into progress
                                            } else {
                                                //如果这个包装函数wrappeHandler是在resolve列表或reject列表中
                                                //newDefer = defer.then(function(){return promise});
                                                //defer在resolve时候执行function(){return promise}的包装函数,在此包装函数中则会执行到此
                                                //即要执行到此,则必须满足 1:此包装函数对应的defer  resolve and  reject,2:hander 返回promise 
                                                // ...and disregard older resolution values
                                                // notify里面返回的promise在resolve后的参数可能会传递给第2个then去执行
                                                // 如果在这之前resolve已经将参数传递给了第2个then,这里要防止老数据
                                                maxDepth++;
                                                /*
                                                * returned.then(resolve( maxDepth, deferred, Identity, special ))
                                                * deferred:下文中的newDefer,作用是Identity()执行后,newDefer.resolveWidth
                                                * */
                                                then.call(
                                                    returned,
                                                    resolve( maxDepth, deferred, Identity, special ),
                                                    resolve( maxDepth, deferred, Thrower, special ),
                                                    resolve( maxDepth, deferred, Identity,
                                                        deferred.notifyWith )
                                                );
                                            }
    
                                        // Handle all other returned values
                                        } else {
                                            // Only substitute handlers pass on context
                                            // and multiple values (non-spec behavior)
                                            /*
                                            * newDefer = defer.then(fn1).then(fn2);
                                            * defer注册fn1,将封装了fn1的包装函数bfn1加入到defer的回调列表中,newDefer注册fn2,同理有bfn2
                                            * 这里是 handler 返回的是普通对象,则newDefer立即resolve的即立即执行fn2
                                            * 如果走defer走resolve流程时,此时fn1 === handler的则newDefer.resolve(fn1的that,fn1的args);
                                            * 如果fn1返回的returned 走resolve流程,此时handler === identity,则newDefer.resolve(undefined,identity的return);
                                            * */
                                            if ( handler !== Identity ) {
                                                that = undefined;
                                                args = [ returned ];
                                            }
    
                                            // Process the value(s)
                                            // Default process is resolve
                                            // 无论then里面的函数返回的promise是notify,resolve,reject,最终都会执行resolveWith
                                            ( special || deferred.resolveWith )( that, args );
                                        }
                                    },
    
                                    // Only normal processors (resolve) catch and reject exceptions
                                    // 这里是对异常的处理??
                                    process = special ?
                                        mightThrow :
                                        function() {
                                            try {
                                                mightThrow();
                                            } catch ( e ) {
    
                                                /*jQuery.Deferred.exceptionHook = function( error, stack ) {
    
                                                 // Support: IE 8 - 9 only
                                                 // Console exists when dev tools are open, which can happen at any time
                                                 if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
                                                 window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
                                                 }
                                                 };*/
    
                                                if ( jQuery.Deferred.exceptionHook ) {
                                                    jQuery.Deferred.exceptionHook( e,
                                                        process.stackTrace );
                                                }
    
                                                // Support: Promises/A+ section 2.3.3.3.4.1
                                                // https://promisesaplus.com/#point-61
                                                // Ignore post-resolution exceptions
                                                // 正常的注册结构出现异常会走下面流程
                                                // TODO 暂不清楚 depth + 1 < maxDepth 的情况
                                                if ( depth + 1 >= maxDepth ) {
    
                                                    // Only substitute handlers pass on context
                                                    // and multiple values (non-spec behavior)
                                                    if ( handler !== Thrower ) {
                                                        that = undefined;
                                                        args = [ e ];
                                                    }
                                                    //正常情况下第一个then的resolve和reject出现异常,会导致第二个then里面的reject执行
                                                    deferred.rejectWith( that, args );
                                                }
                                            }
                                        };
    
                                // Support: Promises/A+ section 2.3.3.3.1
                                // https://promisesaplus.com/#point-57
                                // Re-resolve promises immediately to dodge false rejection from
                                // subsequent errors
                                if ( depth ) {
                                    process();
                                } else {
    
                                    // 最开始的触发
                                    // Call an optional hook to record the stack, in case of exception
                                    // since it's otherwise lost when execution goes async
                                    // TODO 不太明白
                                    if ( jQuery.Deferred.getStackHook ) {
                                        process.stackTrace = jQuery.Deferred.getStackHook();
                                    }
                                    window.setTimeout( process );
                                }
                            };
                        }
                        /*
                        * defer.then(fn)    -->
                        * 创建newDefer        -->
                        * defer关联的回调列表(下文中的tuples[ * ][ 3 ])增加一个fn的包装函数(由resolve生成),
                        * 这个包装函数执行fn,并对其返回值和newDefer做出相应处理    -->
                        * 返回newDefer
                        * */
                        return jQuery.Deferred( function( newDefer ) {
    
                            // progress_handlers.add( ... )
                            tuples[ 0 ][ 3 ].add(
                                resolve(
                                    0,
                                    newDefer,
                                    jQuery.isFunction( onProgress ) ?
                                        onProgress :
                                        Identity,
                                    newDefer.notifyWith
                                )
                            );
    
                            // fulfilled_handlers.add( ... )
                            tuples[ 1 ][ 3 ].add(
                                resolve(
                                    0,
                                    newDefer,
                                    jQuery.isFunction( onFulfilled ) ?
                                        onFulfilled :
                                        Identity
                                )
                            );
    
                            // rejected_handlers.add( ... )
                            tuples[ 2 ][ 3 ].add(
                                resolve(
                                    0,
                                    newDefer,
                                    jQuery.isFunction( onRejected ) ?
                                        onRejected :
                                        Thrower
                                )
                            );
                        } ).promise();    //返回defer的只读版本promise
                    },
    
                    // Get a promise for this deferred
                    // If obj is provided, the promise aspect is added to the object
                    /**
                     * 返回obj的promise对象
                     * @param obj
                     * @returns {*}
                     */
                    promise: function( obj ) {
                        return obj != null ? jQuery.extend( obj, promise ) : promise;
                    }
                },
    
                //内部封装deferred对象
                deferred = {};
    
            // Add list-specific methods
            //给deferred添加切换状态方法
            jQuery.each( tuples, function( i, tuple ) {
                var list = tuple[ 2 ],
                    stateString = tuple[ 5 ];
    
                // promise.progress = list.add
                // promise.done = list.add
                // promise.fail = list.add
                //扩展promise的done、fail、progress为Callback的add方法,使其成为回调列表
                //简单写法:  promise['done'] = jQuery.Callbacks( "once memory" ).add
                // promise['fail'] = jQuery.Callbacks( "once memory" ).add  promise['progress'] = jQuery.Callbacks( "memory" ).add
                promise[ tuple[ 1 ] ] = list.add;
    
                // Handle state
                //切换的状态是resolve成功/reject失败
                //添加首组方法做预处理,修改state的值,使成功或失败互斥,锁定progress回调列表,
                if ( stateString ) {
                    /*
                    if (stateString) {
                     list.add(function(){
                     state = stateString
    
                     //i^1  ^异或运算符  0^1=1 1^1=0,成功或失败回调互斥,调用一方,禁用另一方
                     }, tuples[i^1][2].disable, tuples[2][2].lock)
                     }
                     */
                    list.add(
                        function() {
    
                            // state = "resolved" (i.e., fulfilled)
                            // state = "rejected"
                            state = stateString;
                        },
    
                        // rejected_callbacks.disable
                        // fulfilled_callbacks.disable
                        tuples[ 3 - i ][ 2 ].disable,
    
                        // progress_callbacks.lock
                        tuples[ 0 ][ 2 ].lock
                    );
                }
    
                // progress_handlers.fire
                // fulfilled_handlers.fire
                // rejected_handlers.fire
                list.add( tuple[ 3 ].fire );
    
                // deferred.notify = function() { deferred.notifyWith(...) }
                // deferred.resolve = function() { deferred.resolveWith(...) }
                // deferred.reject = function() { deferred.rejectWith(...) }
                //添加切换状态方法 resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith()
                deferred[ tuple[ 0 ] ] = function() {
                    deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
                    return this;
                };
    
                // deferred.notifyWith = list.fireWith
                // deferred.resolveWith = list.fireWith
                // deferred.rejectWith = list.fireWith
                deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
            } );
    
            // Make the deferred a promise
            //deferred继承promise的执行方法
            promise.promise( deferred );
    
            // Call given func if any
            //传递了参数func,执行
            if ( func ) {
                func.call( deferred, deferred );
            }
    
            // All done!
            //返回deferred对象
            return deferred;
        },
    
        // Deferred helper
        /**
         *
         * 主要用于多异步队列处理。
         多异步队列都成功,执行成功方法,一个失败,执行失败方法
         也可以传非异步队列对象
    
         * @param sub
         * @returns {*}
         */
        when: function( singleValue ) {
            var
    
                // count of uncompleted subordinates
                remaining = arguments.length,
    
                // count of unprocessed arguments
                i = remaining,
    
                // subordinate fulfillment data
                resolveContexts = Array( i ),
                resolveValues = slice.call( arguments ),  //队列数组 ,未传参数是[],slice能将对象转化为数组
    
                // the master Deferred
                // 这是主分支 .when().then(),master决定.then()的执行
                master = jQuery.Deferred(),
    
                // subordinate callback factory
                // .when()参数中每一个value被resolve后调用下面的返回函数
                // 1:将每一个调用者和调用参数存在数组里,2: 最后以数组作为参数,由master.resolve
                updateFunc = function( i ) {
                    return function( value ) {
                        resolveContexts[ i ] = this;
                        // updateFunc()(v1,v2,v3),resolveValues[ i ] = [v1,v2,v3],若只有一个参数,
                        // 则resolveValues[ i ] = value
                        resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
                        if ( !( --remaining ) ) {
                            //如果这是最后一个resolveValues被解决
                            master.resolveWith( resolveContexts, resolveValues );
                        }
                    };
                };
    
            // Single- and empty arguments are adopted like Promise.resolve
            if ( remaining <= 1 ) {
                // 将第二个和第三个参数注册到第一个参数里面去
                // 如果singleValue是常量,则立刻执行master.resolve,下面的判断不会执行
                adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject );
    
                // Use .then() to unwrap secondary thenables (cf. gh-3000)
                //
                if ( master.state() === "pending" ||
                    jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
    
                    return master.then();
                }
            }
    
            // Multiple arguments are aggregated like Promise.all array elements
            // 循环为resolveValues[i] 注册updateFunc()方法 --> 判断计数到最后一个则执行 master.resolve
            // resolveValues[i].reject-->list.add(master.reject);
            while ( i-- ) {
                //    当resolveValues[ i ]为常量时,会立刻执行updateFunc( i ),
                // 如果所有的都为常量,则 执行master.resolve(resolveValues)
                adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
            }
    
            return master.promise();
        }
    } );
    
    return jQuery;
    } );

    以上内容主要是针对then,pipe,when 做了更新与修改,

    pipe:jquery.Deferred(fn)里面会先新建一个newDefer,然后传入newDefer作为参数并执行fn,fn会将pipe里的参数依次加入到回调列表中,并且判断参数返回值是否为promise,若果是,则将newDefer的状态转化器注入到promise的回调列表中,否则,直接让newDefer发生相应的状态转化。

    测试代码如下:

    var defer = $.Deferred();
            defer.pipe(function(data){
                console.log(data);
                var defer1 = $.Deferred();
                setTimeout(function () {
                    defer1.resolve("second pipe");
                },1000);
                return defer1
            }).pipe(function (data) {
                console.log(data);
            });
            defer.resolve("first pipe");
    
    结果:
    first pipe
    second pipe

    then:主要流程如下:

    function then(fnDone,fnFail,fnPro){
                var maxDepth = 0;       //声明调用深度,这里的链式调用实为递归调用
                function resolve()....  //resolve封装了then的参数
    
                var defer = jQuery.Deferred(function (newDefer) {
                    Tuples.add(resolve(fnDone),resolve(fnFail),resolve(fnPro));
                })
            }

    这里主要流程在resolve函数里面,resolve返回一个封装函数fn,这个fn的主要功能是管理和执行resovle中的handle参数,mightThrow 方法主要实现基本的promise功能

    如果fnDone和fnFail出现了异常,则捕获异常,并newDefer.reject(); 

    如下例子:

    var defer = $.Deferred();
                defer.then(function () {
                    throw new Error("resolve error");
                }).then(function () {
                    console.log("second then resolve");
                },function (data) {
                    console.log("second then reject");
                    console.log(data);
                });
                defer.resolve();
    
    结果:
    second then reject
     Error: resolve error
        at file:///defer.html:10:19
        at jQuery.extend.Deferred.promise.then.mightThrow (file:///jquery-3.0.0.js:3507:29)
        at jQuery.extend.Deferred.promise.then.process (file:///jquery-3.0.0.js:3576:12)

    mightThrow方法 首先执行 returned = handler.apply(that,args);  如果returned的类型是promise,则将newDefer的状态转换交给returned的回调列表进行管理,若为常量,则直接调用newDefer.resolve(); 从pending状态到resolve状态其中的流程大多是不可控的,为避免pending的数据影响resolve ,于是用depth来区分。

    defer.when: 传一个或一组参数,可以是常量也可以是promise,这里有两个地方我觉得是非常好的,第一个是把when中每个参数完成后的计数操作提取出来形成一个函数

    updateFunc(i),第二个是adoptValue ,将master的状态变化注入到每个参数的回调列表中由其统一管理。

    如下例子:

    $.when("no-promise").then(function (data) {
                console.log(data + "Execution without delay!");
    });
    结果:
    no-promiseExecution without delay!
    
    var defer1 = $.Deferred();
            var defer2 = $.Deferred();
            var defer3 = $.Deferred();
    
            $.when(defer1,defer2,defer3).then(function (d1,d2,d3) {
                console.log(d1);
                console.log(d2);
                console.log(d3);
            });
    
            setTimeout(function () {
                defer1.resolve("defer1");
            },1000);
            setTimeout(function () {
                defer2.resolve("defer2");
            },2000);
            setTimeout(function () {
                defer3.resolve("defer3");
            },3000);
    
    结果:
    defer1
    defer2
    defer3

    注:本人小菜一枚,若有不通之处,敬请指教

    
    

      

    
    
    
    
  • 相关阅读:
    文档视图
    引入缓冲池技术
    数据库访问与查询
    OnInitialUpdate函数
    显示股票视图的全局函数
    切换视图的核心代码
    GuiEdit的使用
    操作方法
    SQL 使用 解析
    调用API 实现 窗体 拖动
  • 原文地址:https://www.cnblogs.com/zhutao/p/5630020.html
Copyright © 2011-2022 走看看