js-jQuery中的Callbacks、Deferred和When对象详解(1)

来源:互联网 发布:文华行情软件 编辑:程序博客网 时间:2024/06/05 22:42

终于抽出点时间来讲解一下jQuery中的Callbacks/Deferred这几个对象了,不过篇幅可能过长,讲到哪儿是哪儿吧。

$.Deferred方法每次调用都会返回一个Deferred对象。Deferred对象用于异步方法链式调用,让编程方式从传统的回调函数中解脱出来——javascript的回调深度一直都是遭人诟病。

首先从最底层Callbacks对象讲起,看简化后的源码:

Callbacks: function(options){var list = [],firing, fired, memory,firingIndex, firingStart, firingLength,options = options || {},stack = !options.once && [],fire = function(data){memory = options.memory && data;firingIndex = firingStart || 0;firingStart = 0;fired = true;firingLength = list.length;firing = true;for(; firingIndex < firingLength; firingIndex++){if(list[firingIndex].apply(data[0], data[1]) === false && options.stopOnFalse === true){memory = false; // To prevent further calls using addbreak;}}firing = false;if(list){if(stack){if(stack.length){fire(stack.shift());}}else if(memory){list = [];//if options.once then disable it.}else{self.disable();}}},self = {add: function(){if(list){var start = list.length;(function _add(args){iBen.each(args, function(arg){if(jQuery.isFunction(arg)){list.push(arg);} else if ( arg && arg.length && !jQuery.isString(arg) ) {// Inspect recursively_add( arg );}});})(arguments);if(firing){firingLength = list.length;}else if(memory){firingStart = start;fire(memory);}}return this;},// Remove a callback from the listremove: function() {if ( list ) {jQuery.each( arguments, function( _, arg ) {var index;while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {list.splice( index, 1 );// Handle firing indexesif ( firing ) {if ( index <= firingLength ) {firingLength--;}if ( index <= firingIndex ) {firingIndex--;}}}});}return this;},fireWith: function(context, args){if(list){args = [context, args];if(firing){stack.push(args);}else{fire(args);}}return this;},fire: function(){return this.fireWith(this, arguments);},disable: function(){list = stack = memory = undefined;return this;}};return self;}

我就不逐行写注释了,简单来说最后返回的self才是真正的Callbacks对象,公布出的接口主要也就是add、fire、fireWith、remove这几个方法。

add方法将函数添加到回调列表list中。

_add方法在参数是数组的时候会递归调用。

如果正在调用的话,修正回调列表的长度。

如果不是调用中且没有指明once选项同时指定了memory选项去记住最后一次调用的参数时则修正开始调用索引去立即调用。

fire方法实际调用的是Callbacks中的fire方法。

调用fireWith-指定默认环境为self和参数。

将参数进行格式化,如果正在调用则放到stack中等待当前调用列表完成后调用,否则立即调用。

进行fire时会通过options.once判断改列表是否只调用一次,是则调用结束后disable掉该回调列表,否则查看stack中是否有函数等待回调,如果指定了memory选项去记录了最后一次调用的参数,则清空该回调列表。

remove方法将对应的函数从回调列表中移除,并修正firing的索引。


整个Callbacks的流程就是这样,其实Callbacks可以看做为一个观察者模式。看观察者模式的代码:

observer : (function(box){var Publisher={subscribe: function(fn, context, type){type = type || 'any';fn = iBen.isFunction(fn) ? fn: context[fn];if (iBen.isUndefined(this.subscribers[type]))this.subscribers[type] = [];this.subscribers[type].push([fn, context]);},unsubscribe: function(fn, type){type = type || 'any';var that = this;this.subscribers[type].splice($A(this.subscribers[type]).indexOf(fn), 1);},shutdown: function(type){type = type || 'any';this.subscribers[type] = [];},publish: function(){var args, type = arguments[arguments.length-1];type = iBen.isString(type)? type : 'any';args = _slice.call(arguments, 0);this.subscribers[type]&&iBen.each(this.subscribers[type], function(object){object[0].apply(object[1], args);});}};return function(o){o = o || {};iBen.extend(o, Publisher, true, 'function');o.subscribers={any : []};return o;};})()
基本上工作的流程是一样的,都是将对应的事件添加到队列中去,然后通过fire/publish的方式调用整个回调列表,除了Callbacks对象可以在函数的执行中通过self对象提供的接口更细粒度的控制和获取回调列表的运行状态。

Callbacks对象相对比较直观,下面看jQuery的Deferred对象,不过在这之前最好先自行了解一下Promise/A+规范:

要求

Promise的状态

一个 Promise 必须处于等待态(Pending)执行态(Fulfilled)拒绝态(Rejected)这三种状态中的一种之中。

  1. 处于等待态时,promise :

    • 可以迁移至执行态或拒绝态
  2. 处于执行态时,promise :

    • 不能迁移至其他任何状态
    • 必须拥有一个不可变的值
  3. 处于拒绝态时,promise

    • 不能迁移至其他任何状态
    • 必须拥有一个不可变的据因

这里的不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变

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;return jQuery.Deferred(function( newDefer ) {jQuery.each( tuples, function( i, tuple ) {var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];// deferred[ done | fail | progress ] for forwarding actions to newDeferdeferred[ tuple[1] ](function() {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 {newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );}});});fns = null;}).promise();},promise: function( obj ) {return obj != null ? jQuery.extend( obj, promise ) : promise;}},deferred = {};jQuery.each( tuples, function( i, tuple ) {var list = tuple[ 2 ],stateString = tuple[ 3 ];// promise[ done | fail | progress ] = list.addpromise[ tuple[1] ] = list.add;// Handle stateif ( 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 ]deferred[ tuple[0] ] = function() {deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );return this;};deferred[ tuple[0] + "With" ] = list.fireWith;});promise.promise( deferred );if ( func ) {func.call( deferred, deferred );}// All done!return deferred;}

可以看出,jQuery并没有完全按照Promise规范来构建promise对象。jQuery的三种状态分别为:resolved,rejected和处理中状态,而对应promise规范中的执行状态和被拒绝状态时状态不可改变的要求:
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 );}
i^1会讲0和1的索引对调(在我的另一篇博文中有提到),在执行resolve时会将reject列表disable掉,同样在执行reject中会disable掉resolve列表,并将notify列表lock住,以防止状态的改变。

Deferred对象里面主要包含两个部分:

1、promise对象

在遍历tuples时会将对应的Callbacks对象的add方法作为promise的done | fail | progress。

2、deferred对象

在遍历tuples时会将对应的Callbacks对象的fireWith方法作为resolveWith | rejectWith | notifyWith

调用promise.promise将deferred对象做成一个promise对象。

最后返回deferred对象。

大概讲到这里吧,剩下的then方法和when对象留到下次讲解。

有其他疑问或错误之处欢迎之处~

0 0
原创粉丝点击