jQuery 延迟对象源码

 

Deferred延迟对象

 

	jQuery.extend({

		Deferred : function () {
			;
		}

		when : function () {
			;
		}

	});

扩展了2个工具方法。
延迟对象,是基于回调函数开发的。
$.Deferred(); -> $.Callbacks();
$.when();

 


复习一下Callbacks:

 

例子(1var cb = $.Callbacks();
    setTimeout(function () {
        alert(111);
        cb.fire();
    }, 1000);


    cb.add(function () {
        alert(222);
    })

    //先弹出111 ,然后是222.

 

 

延迟对象Deferred

 

例子(2)
	var dfb = $.Deferred();
	setTimeout(function () {
		alert(111);
		dfb.resolve();			//完成
	}, 1000);


	dfb.done(function () {		     //成功
		alert(222);
	})

	//先弹出111 ,然后是222.

 

如果是
例子(3)
	setTimeout(function () {
		alert(111);
	}, 1000);

	alert(222);

	//这就先弹出222, 在弹出111.


如果有一个需求,就是先弹出111,再弹出222。
如果将alert(222);放入alert(111)后面。也是一个解决办法,
但是这样的办法,存在作用域的问题。就变成内部作用域了。

这个时候选用延迟对象,就是很好的方式了。

例子(4)
	var dfb = $.Deferred();
	setTimeout(function () {
		alert(111);
		dfb.reject();			//未完成
	}, 1000);


	dfb.fail(function () {		//失败
		alert(222);
	})

	//先弹出111 ,然后是222.

还有一个进行时:

例子(5)
	var dfb = $.Deferred();
	setTimeout(function () {
		alert(111);
		dfb.notify();				//运行时
	}, 1000);


	dfb.progress(function () {		//动态执行
		alert(222);
	})

 


一共三组对应:
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]

 

 

例子(6)
$.ajax({
    url : 'xxx.php',
    success : function () {
        alert('success');
    },
    error : function () {
        alert('fail');
    }
});


//可以通过这个去写。成功的时候触发done,失败触发fail
$.ajax('xxx.php').done(function () {
    alert('success');
}).fail(function () {
    alert('fail');
});

 

 

 

 

resolve() 和 reject() -->>> fire()
done() 和 fail() -->>> add()

 

这个复合数组定义了对应的映射状态。
        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") ]
            ],

    这里注意第三个:memory是有记忆功能,而once是只能触发一次。这个在callbacks中我有分析道。
在这里的运行中,是可以多次运行的。因为无论是成功还是失败,都是已经执行结束了。而"notify", "progress"可以执行多次。

数组的三个对,每一行有三个参数。最后一个是创建回调对象,完成回调方法。

这里是对数组进行了匹配和分离。
    状态就是fire
    回调的方法,就是add
源码:
    // Add list-specific methods
        jQuery.each( tuples, function( i, tuple ) {
            var list = tuple[ 2 ],            // 数组的第二项是回调对象。
                stateString = tuple[ 3 ];    // 第三项是状态

            // promise[ done | fail | progress ] = list.add,
            // 这个注释阐述了回调对象的add对应着三个状态:成功,失败和进行中。这就是回调的方法。 
            promise[ tuple[1] ] = list.add;

            // Handle state
            if ( stateString ) {
                list.add(function() {
                    // state = [ resolved | rejected ]
                    state = stateString;

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

            // deferred[ resolve | reject | notify ]
            // 这个延迟对象加入了状态所对应的函数。
            // 这个状态,调用的是 fireWith 这个Callback的方法
            deferred[ tuple[0] ] = function() {
                deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
                return this;
            };
            deferred[ tuple[0] + "With" ] = list.fireWith;
        });

        // Make the deferred a promise
        promise.promise( deferred );

        // Call given func if any
        if ( func ) {
            func.call( deferred, deferred );
        }

        // All done!
        return deferred;
    },

 

例子(7var dfb = $.Deferred();
    setTimeout(function () {
        alert(111);
        dfb.resolve();
    }, 1000);


    dfb.done(function () {
        alert('成功');
    }).fail(function () {
        alert('失败')
    })

    弹出111后。然后弹出成功。也就证明是resolve对应着done。
如果写入reject,就对应着fail。

例子(8)
    
    解释一下[ "notify", "progress", jQuery.Callbacks("memory") ]与其他两者的不同。

    var dfb = $.Deferred();
    setInterval(function () {
        alert(111);
        dfb.resolve();
    }, 1000);


    dfb.done(function () {
        alert('成功');
    }).fail(function () {
        alert('失败')
    })

    //    这里的实验结果就是第一次弹出 111, 然后弹出 ‘成功’;
    //    第二次以后只弹出 111.

    对比

    var dfb = $.Deferred();
    setInterval(function () {
        alert(111);
        dfb.notify();
    }, 1000);


    dfb.done(function () {
        alert('成功');
    }).fail(function () {
        alert('失败')
    }).progress(function () {
        alert('进行中。。');
    });

    //    这个就是多次弹出 111 和 进行中。。。
    //    总是成对出现。

状态这个东西,只要一触发,就是结束了,没必要连续触发。
成功就是成功了。失败就是失败了。

在分析一次 memory 的问题。就是记忆功能功能。在之前的callbacks分析过了。
这里在写一次。

例子(9var cb = $.Callbacks();
    cb.add(function () {
        alert(1);
    })
    cb.fire();

    cb.add(function () {
        alert(2);
    })
//这里就只能弹出    -->> 1

    
    var cb = $.Callbacks('memory');
    cb.add(function () {
        alert(1);
    })
    cb.fire();

    cb.add(function () {
        alert(2);
    })
// 这里可以弹出 -->> 1 , 2
    在fire的时候,他有记忆功能,把所有的add添加的函数,都调用。

例子(10<input type="button" value ='点击' id='but' />

    var cb = $.Callbacks('memory');
    cb.add(function () {
        alert(1);
    });

    cb.fire();

    var but = document.getElementById('but');
    but.onclick = function () {
        cb.add(function () {
            alert(2);
        });
    }

    //    这里先执行 1. 然后点击以后,执行 2,


//这里都有memery,所以延迟对象,都有记忆功能。

    var dfb = $.Deferred();
    setTimeout(function () {
        alert(111);
        dfb.resolve();
    }, 1000);


    dfb.done(function () {
        alert('成功 aaa');
    });

    var but = document.getElementById('but');
    but.onclick = function () {
        dfb.done(function () {
            alert('bbb');
        });
    }

    //    先执行 111 —— >> resolve()执行,然后调用done alert('aaa')
    //    然后,点击事件的时候,立即执行 bbb。
    //    利用这个特性,就第一次不去触发执行,以后每一次都去触发执行的特性

 

 

promise 和 deferred : 两个数组,一个定义了add ,另一个对应着firewith

   // Make the deferred a promise

  promise.promise( deferred ); //这里就是将 promise所有的方法,继承给deferred对象。
函数执行以后。导致的结果,就是deferred比promise多出了三个方法。[resolve | reject | notify];

 

 

例子(11function A() {
        var dfb = $.Deferred();
        setTimeout(function () {
            alert(111);
            dfb.resolve();
        }, 1000);

        return dfb;
    }

    var df = A();

    df.done(function () {
        alert('成功 aaa');
    }).fail(function () {
        alert('fail bbb');
    });

    df.reject();

    //    执行结果,先执行失败,fail bbb
    //    在执行111
    //     原因是,因为 df.reject(); 先执行,而后,直接就执行了fail, 1秒钟以后,才执行了计时函数,

那如何让外面不能修改呢?
    很简单,promise。(这个属性已经在ES6中有独立出来了,就是解决回调问题的。)
    
    function A() {
        var dfb = $.Deferred();
        setTimeout(function () {
            alert(111);
            dfb.resolve();
        }, 1000);

        return dfb.promise();    //这里加上了promise,就变成promise对象。
        //    注意啊,promise    是没有    [resolve | reject | notify] 这三个方法的,
        //    所以,根本无法改变状态。
        //    没有参数,就返回promise对象了。
    }

    var df = A();

    df.done(function () {
        alert('成功 aaa');
    }).fail(function () {
        alert('fail bbb');
    });

    df.reject();    //这里报错,找不到这个函数。肯定找不到啊。因为promise没有这个函数。

    //    执行结果,这回就是返回成功和111.并且,reject还会报错。
    //    因为用了promise之后,就不能再进行修改状态了。



例子(12)
    查看状态:obj.state();
    并传入参数

    function A() {
        var df = $.Deferred();
        alert (df.state());        //pending

        setTimeout(function () {
            alert(111);
            df.resolve('hg');    //传入参数
        }, 1000);

        return df.promise();    //这里加上了promise
    }

    var df = A();

    df.done(function () {
        alert('成功 aaa');
        alert(arguments[0]);    //可以获取传入的参数
        alert (df.state());        //resolve

    }).fail(function () {
        alert('fail bbb');
        alert (df.state());
    });


源码:
    // Handle state
    if ( stateString ) {
        list.add(function() {
            // state = [ resolved | rejected ]
            state = stateString;

        // [ reject_list | resolve_list ].disable; progress_list.lock
        //    这里就是三选一,选择了一个,另外两个状态就不会触发。 ^ 是位运算符。
        }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
    }


例子(13var df = $.Deferred();

    setTimeout(function () {
        df.resolve('hg');

    }, 1000);

    var newDf = df.pipe(function () {
        return arguments[0] + " wj";
    });

    newDf.done(function () {
        alert(arguments[0]);
    });

    //    打印出 hg wj
    //    pipe的返回值,作为新的状态转移的参数。返回一个新的不能修改状态的延迟对象。
    //     pipe不影响状态,只是影响参数。

 

 

 

总结一下:
    promise。缺少三种状态,只要延迟对象调用promise()方法,就是不能修改状态了。

    promise {
        //方法。
        state     :    状态。
            有state()的方法,获取状态

        always    :     总是触发,不管是你完成,还是未完成,都是触发。
                always: function() {
                    deferred.done( arguments ).fail( arguments );
                    return this;
                },

        then    :  /* fnDone, fnFail, fnProgress */  分开写。
                obj,then(function () {
                    //fnDone
                }, function () {
                    //fnFail
                },function () {
                    //fnProgress
                });    

        promise        //如果有参数,就将promise下面的所有方法,都继承给obj对象。
                    promise: function( obj ) {
                        return obj != null ? jQuery.extend( obj, promise ) : promise;
                    }

        pipe    :    管道,给参数做个处理。并不影响状态转移。
                    可以理解为继承的延迟对象。
                    // Keep pipe for back-compat
                    promise.pipe = promise.then;

        done | fail | progress;
    }


    deferred {
        //方法
        resolve | reject | notify + promise对象内的属性和方法。
}

 

 

posted on 2015-10-07 02:52  HGonlyWJ  阅读(232)  评论(0编辑  收藏  举报

W3C中国
阮老师的网络日志
canvas
runoob
迷渡
并发编程网
原生JS例子
前端外刊评论