jQuery在1.5引入了Deferred对象(异步列队),当时它还没有划分为一个模块,放到核心模块中。直到1.52才分割出来。它拥有三个方法:_Deferred, Deferred与when。
出于变量在不同作用域的共用,jQuery实现异步列队时不使用面向对象方式,它把_Deferred当作一个工厂方法,返回一个不透明的函数列队。之所以说不透明,是因为它的状态与元素都以闭包手段保护起来,只能通过列队对象提供的方法进行操作。这几个方法分别是done(添加函数),resolveWith(指定作用域地执行所有函数),resolve(执行所有函数),isResolved(判定是否已经调用过resolveWith或resolve方法),cancel(中断执行操作)。但_Deferred自始至终都作为一个内部方法,从没有在文档中公开过。
Deferred在1.5是两个_Deferred的合体,但1+1不等于2,它还是做了增强。偷偷爆料,Deferred本来是python世界大名鼎鼎的Twisted框架的东西,由早期七大JS类库中的MochiKit取经回来,最后被dojo继承衣钵。jQuery之所以这样构造Deferred,分明不愿背抄袭的恶名,于是方法改得一塌糊涂,是jQuery命名最差的API,完全不知所云。它还加入当时正在热烈讨论的promise机制。下面是一个比较列表:
dojo | jQuery | 注解 |
addBoth | then | 同时添加正常回调与错误回调 |
addCallback | done | 添加正常回调 |
addErrback | fail | 添加错误回调 |
callback | done | 执行所有正常回调 |
errback | reject | 执行所有错误回调 |
doneWith | 在指定作用域下执行所有正常回调,但dojo已经在addCallback上指定好了 | |
rejectWith | 在指定作用域下执行所有错误回调,但dojo已经在addErrback上指定好了 | |
promise | 返回一个外界不能改变其状态的Deferred对象(外称为Promise对象) |
jQuery的when方法用于实现回调的回调,或者说,几个异列列队都执行后才执行另外的一些回调。这些后来的回调也是用done, when, fail添加的,但when返回的这个对象已经添加让用户控制它执行的能力了。因为这时它是种叫Promise的东西,只负责添加回调与让用户窥探其状态。一旦前一段回调都触发了,它就自然进入正常回调列队(deferred ,见Deferred方法的定义)或错误回调列队(failDeferred )中去。不过我这样讲,对于没有异步编程经验的人来说,肯定听得云里雾里。看实例好了。
$.when({aa:1}, {aa:2}).done(function(a,b){
console.log(a.aa)
console.log(b.aa)
});
直接输出1,2。如果是传入两个函数,也是返回两个函数。因此对于普通的数据类型,前面的when有多少个参数,后面的done, fail方法的回调就有多少个参数。
function fn(){
return 4;
}
function log(s){
window.console && console.log(s)
}
$.when( { num:1 }, 2, '3', fn() ).done(function(o1, o2, o3, o4){
log(o1.num);
log(o2);
log(o3);
log(o4);
});
如果我们想得到各个异步的结果,我们需要用resolve, resolveWith, reject, rejectWith进行传递它们。
var log = function(msg){
window.console && console.log(msg)
}
function asyncThing1(){
var dfd = $.Deferred();
setTimeout(function(){
log('asyncThing1 seems to be done...');
dfd.resolve('1111');
},1000);
return dfd.promise();
}
function asyncThing2(){
var dfd = $.Deferred();
setTimeout(function(){
log('asyncThing2 seems to be done...');
dfd.resolve('222');
},1500);
return dfd.promise();
}
function asyncThing3(){
var dfd = $.Deferred();
setTimeout(function(){
log('asyncThing3 seems to be done...');
dfd.resolve('333');
},2000);
return dfd.promise();
}
/* do it */
$.when( asyncThing1(), asyncThing2(), asyncThing3() ).done(function(res1, res2, res3){
log('all done!');
log(res1 ', ' res2 ', ' res3);
})
异步列队一开始没什么人用(现在也没有什么人用,概念太抽象了,方法名起得太烂了),于是它只能在内部自产自销。首先被染指的是queue。queue模块是1.4为吸引社区的delay插件,特地从data模块中分化的产物,而data则是从event模块化分出来的。jQuery新模块的诞生总是因为用户对已有API的局限制不满而致。最早的queue模块的源码:
jQuery.extend({
queue: function( elem, type, data ) {
if ( !elem ) {
return;
}
type = (type || "fx") "queue";
var q = jQuery.data( elem, type ); // 如果只是查找,则可以快速出队
if ( !data ) {
return q
}
if ( !q || jQuery.isArray(data) ) {
q = jQuery.data( elem, type, jQuery.makeArray( data) );
} else {
q.push(
}
返回 q;
},
出队: function( elem, type ) {
type = type || "fx";
var queue = jQuery.queue( elem, type ), fn = queue.shift(); // 如果 fx 队列出队,则始终移除进度哨兵
if ( fn === "inprogress" ) {
fn = queue.shift();
if ( fn ) {
// 添加进度哨兵以防止 fx 队列
// 自动出队
if ( type === "fx" ) {
queue.unshift("inprogress");
}
fn.call(elem, function( ) {
jQuery.dequeue(elem, type)
});
jQuery.fn.extend({
queue: function( type, data ) {
if ( typeof type !== "string" ) {
data = type;
type = " fx";
}
if ( data === undefined ) {
return jQuery.queue( this[0], type );
}
return this.each(function( i , elem ) {
var queue = jQuery.queue( this, type, data );
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery .dequeue( this, type );
}
});
出队: function( type ) {
return this.each(function() {
jQuery. dequeue( this, type );
});
// 基于 Clint Helfers 的插件,经许可。 php/2009/07/jquery-delay/
延迟:函数( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;类型 || "fx";
返回 this.queue( type, function() {
var elem = this;
setTimeout(function() {
jQuery.dequeue( elem, type );
}, 时间);
});
clearQueue: function( type ) {
return this.queue( type || "fx", [] ); >}
});
1.6 添加了_mark,_unmark,promise。queue 是让函数同属一个队伍里面,目的是让动画一个接一个执行。_mark 顶让他们各自拥有队伍,并列执行(虽然其中只记录异步列队中已被执行的函数个数)。promise则在这些并发执行的动画执行后才执行另外一些回调(或动画)。
复制代码
代码如下:
(function( jQuery ) {
function handleQueueMarkDefer( elem, type, src ) {
//清空记录deferred个数的队字段,函数列队与异步列
var deferDataKey = type "defer",
queueDataKey = type "queue",
markDataKey = type "mark",
defer = jQuery.data( elem, deferDataKey, undefined, true );
if ( defer &&
( src === "queue" || !jQuery.data( elem, queueDataKey, undefined, true ) ) &&
( src === "mark" || !jQuery.data( elem, markDataKey, undefined , true ) ) ) {
// 为硬编码回调提供空间,以便首先触发
// 并最终在元素上标记/排队其他内容
setTimeout( function() {
if ( !jQuery.data( elem,queueDataKey, undefined, true ) &&
!jQuery.data( elem, markDataKey, undefined, true ) ) {
jQuery.removeData( elem, deferDataKey, true ); .resolve();
}
}, 0
}
}
jQuery.extend({
_mark: function( elem, type ) {
if ( elem ) {
type = (type || "fx") "mark";//创建一个以mark为后缀的字段,用于记录此列队中的个数
jQuery.data( elem, type, (jQuery.data(elem,类型,未定义,true) || 0) 1, 正确);
}
},
_unmark: function(force, elem, type ) {
if (force !== true ) {
type = elem;
elem = 力;
力=假;
}
if ( elem ) {
type = type || “fx”;
var key = type "mark",
//让个数减1,如果第一个参数为true,就强逼减至0
count = force ? 0 : ( (jQuery.data( elem, key, 未定义, true) || 1 ) - 1 );
if ( count ) {
jQuery.data( elem, key, count, true );
} else {//如果为0,就移除它
jQuery.removeData( elem, key, true );
handleQueueMarkDefer( elem, type, "mark" );
}
}
},
队列: function( elem, type, data ) {
if ( elem ) {
type = (type || "fx") "queue ”;
var q = jQuery.data( elem, type, undefined, true );
// 如果这只是一个查找,则通过快速退出来加速出队
if ( data ) {
if ( !q || jQuery.isArray(data) ) {
q = jQuery.数据( elem, 类型, jQuery.makeArray(data), true );
} else {
q.push( 数据 );
}
}
返回q || [];
}
},
出队: function( elem, type ) {
type = type || “fx”;
var queue = jQuery.queue( elem, type ),
fn = queue.shift(),
defer;
// 如果 fx 队列出队,总是删除进度标记
if ( fn === "inprogress" ) {
fn = queue.shift();
}
if ( fn ) {
// 添加进度哨兵,防止 fx 队列被
// 自动出列
if ( type === "fx" ) {
queue.unshift("进行中");
}
fn.call(elem, function() {
jQuery.dequeue(elem, type);
});
}
if ( !queue.length ) {
jQuery.removeData( elem, type "queue", true );
handleQueueMarkDefer( elem, type, "queue" );
}
}
});
jQuery.fn.extend({
queue: function( type, data ) {
if ( typeof type !== "string" ) {
data = type;
type = " fx";
}
if ( data === undefined ) {
return jQuery.queue( this[0], type );
}
return this.each(function() {
var queue = jQuery.queue( this, type, data );
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue(这个,类型 );
}
});
出队:函数( type ) {
返回 this.each(function() {
jQuery.dequeue( this , type );
},
// 基于 Clint Helfers 的插件,经许可。 /07/jquery-delay/
延迟:函数( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; "fx";
return this.queue( type, function() {
var elem = this;
setTimeout(function() {
jQuery.dequeue( elem, type );
}, 时间 );
},
clearQueue: function( type ) {
return this.queue( type || "fx",
},
//把 jQuery 对象装进一个异步列队,允许它在一系列动画中再执行之后绑定的回调
promise: function( type, object ) {
if ( typeof type !== "字符串" ) {
对象 = 类型;
类型=未定义;
}
类型 = 类型 || “fx”;
var defer = jQuery.Deferred(),
elements = this,
i = elements.length,
count = 1,
deferDataKey = type "defer",
queueDataKey =输入“队列”,
markDataKey = 输入“标记”;
函数resolve() {
if ( !( --count ) ) {
defer.resolveWith( elements, [ elements ] );
}
}
while( i-- ) {
//如果之前已经使用过unmark、queue等方法,那么我们将生成一个新的延迟传感器硬盘系统
if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
( jQuery.data( elements[ i ],queueDataKey, undefined, true ) ||
jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
jQuery.data( elements[ i ], deferDataKey, jQuery._Deferred(), true ) )) {
count ;
tmp.done( 解决 );
}
}
resolve();
返回 defer.promise();
}
});
})( jQuery );
jQuery.ajax模块也被染指,$.XHR对象,当作HTTPXMLRequest对象的仿造器是由一个Deferred对象与一个_Deferred的对象构成。
deferred = jQuery.Deferred(),
completeDeferred = jQuery._Deferred(),
jqXHR ={/**/}
//....
deferred.promise( jqXHR );
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;
jqXHR.complete = completeDeferred.done;
jQuery1.7,从deferred模块中分化出callback模块,其实就是之前的_Deferred的增强版,添加去重,锁定,return false时中断执行下一个回调,清空等功能。
(function( jQuery ) {
// 字符串到对象标志格式缓存
var flagsCache = {};
// 将字符串格式的标志转换为对象格式的标志并存储在缓存
function createFlags( flags ) {
var object = flagsCache[ flags ] = {},
i, length;
flags = flags.split( /s / ); i = 0, length = flags.length; i
object[ flags[i] ] = true;
返回对象;
* 使用以下参数创建回调列表:
*
* 标志:以空格分隔的标志的可选列表,将更改
* 回调列表的行为方式
*
* 默认情况下,回调列表将像事件回调列表一样,并且可以
*“触发”多次
*
* 可能的标志:
*
*一次:将确保回调列表只能被触发一次(如延迟)
*
* 内存:将跟踪以前的值,并在列表被立即触发后调用任何添加的回调
* 最新的“记忆”
*值(如延迟)
*
*唯一:将确保回调只能添加一次(列表中没有重复)
*
* stopOnFalse:中断调用当回调返回 false
*
*/
jQuery.Callbacks = function( flags ) {
// 将标志从字符串格式转换为对象格式
// (我们签入首先缓存)
flags = flags ? ( flagsCache[ 标志 ] || createFlags( 标志 ) ) : {};
var // 实际回调列表
list = [],
// 可重复列表的 fire 调用堆栈
stack = [],
// 最后的 fire 值(用于不可忘记的) list)
memory,
// 标记以了解列表当前是否正在触发
firing,
// 第一个触发回调(由 add 和 fireWith 内部使用)
firingStart,
// 触发时循环结束
firingLength,
// 当前触发回调的索引(如果需要,可通过删除进行修改)
firingIndex,
// 将一个或多个回调添加到列表
add = function( args ) {
var i,
长度,
elem,
类型,
实际;
for ( i = 0, length = args.length; i elem = args[ i ];
type = jQuery.type( elem );
if ( type === "array" ) {
// 递归检查
add( elem );
} else if ( type === "function" ) {
// 添加 if 不是唯一模式并且回调不在
if ( !flags.unique || !self.has( elem ) ) {
list.push( elem );
}
}
}
},
// 触发回调
fire = function( context, args ) {
args = args || [];
内存 = !flags.内存 || [上下文,参数];
开火= true;
fireingIndex = fireStart || 0;
点火开始 = 0;
fireingLength = list.length;
for ( ; list &&fireingIndex
内存 = true; // 标记为停止
break;
}
}
开火= false;
if ( list ) {
if ( !flags.once ) {
if ( stack && stack.length ) {
内存 = stack.shift();
self.fireWith( 内存[ 0 ], 内存[ 1 ] );
}
} else if (内存 === true ) {
self.disable();
} else {
list = [];
}
}
},
// 实际回调对象
self = {
// 将回调或回调集合添加到列表
add: function( ) {
if ( list ) {
var length = list.length;
添加(参数);
// 我们是否需要将回调添加到
// 当前触发批次?
if (fireing) {
firingLength = list.length;
// 有了内存,如果我们没有开火,那么
// 我们应该立即调用,除非之前的
// 开火被停止 (stopOnFalse)
} else if ( memory && memory ! == true ) {
firingStart = 长度;
火(内存[0],内存[1]);
}
}
返回此;
},
// 从列表中删除回调
remove: function() {
if ( list ) {
var args = arguments,
argIndex = 0,
argLength = args.length;
for ( ; argIndex for ( var i = 0; i if ( args[ argIndex ] === list[ i ] ) {
// 处理firingIndex 和firingLength
if (firing ) {
if ( i firingLength--;
if ( i fireingIndex--;
}
}
}
//删除元素
list.splice( i--, 1 );
// 如果我们有一些唯一性属性,那么
// 我们只需要执行一次
if ( flags.unique ) {
break;
}
}
}
}
}
返回此;
},
// 控制给定回调是否在列表中
has: function( fn ) {
if ( list ) {
var i = 0,
length =列表长度;
for ( ; i if ( fn === list[ i ] ) {
返回 true;
}
}
}
返回 false;
},
// 从列表中删除所有回调
empty: function() {
list = [];
返回这个;
},
// 让列表不再执行任何操作
disable: function() {
list = stack = memory = undefined;
返回这个;
},
//是否禁用?
禁用:function() {
return !list;
},
// 将列表锁定在当前状态
lock: function() {
stack = undefined;
if ( !内存 || 内存 === true ) {
self.disable();
}
返回此;
},
//是否已锁定?
锁定: function() {
return !stack;
},
// 使用给定的上下文和参数调用所有回调
fireWith: function( context, args ) {
if ( stack ) {
if (fireing ) {
if ( !flags.once ) {
stack.push( [ context, args ] );
}
} else if ( !( flags.once && memory ) ) {
fire( context, args );
}
}
返回此;
},
// 使用给定参数调用所有回调
fire: function() {
self.fireWith( this,arguments );
返回这个;
},
// 了解回调是否已被调用至少一次
fired: function() {
return !!memory;
}
};
返回自我;
};
})( jQuery );
这期间还有个小插曲,jQuery 团队还想增加一个叫主题的模块,内置发布者订阅者机制,但是太封装了,结果被否决。
(function( jQuery ) {
var topic = {},
sliceTopic = [].slice;
jQuery.Topic = function( id ) {
var 回调,
方法,
topic = id && 主题[ id ]; topic ) {
callbacks = jQuery.Callbacks()
topic = {
发布:callbacks.fire,
订阅:callbacks.add,
取消订阅:callbacks.remove
} ;
if ( id ) {
topics[ id ] = topic;
}
返回主题
}
jQuery.extend({
订阅: function( id ) {
var topic = jQuery.Topic( id ),
args = sliceTopic.call(args, 1 );
topic.subscribe.apply( topic, args ); return {
主题:主题,
args
};
取消订阅:函数( id ) {
var topic = id && id.topic || jQuery.主题( id );
topic.unsubscribe.apply( topic, id && id.args ||
sliceTopic.call( 参数, 1 ) );
},
发布: 函数( id ) {
var topic = jQuery.Topic( id );
topic.publish.apply( topic, sliceTopic.call(arguments, 1 ) );
}
});
})( jQuery );
虽然把大量代码移动回调,但1.7的Deferred却一点没变小,它变得更重型,由三个函数列队组成了。并且返回的是Promise对象,比原来多长了pipe, state,progress,always方法。ajax端就变成这样:
复制代码
jqXHR.error = jqXHR.fail;
jqXHR.complete =completeDeferred.add;
队列两边也变了多少。
复制代码
(function( jQuery ) {
function handleQueueMarkDefer( elem, type, src ) {
var deferDataKey = type "defer",
queueDataKey = type "queue",
markDataKey = type "mark",
defer = jQuery._data( elem, deferDataKey );
if ( defer &&
( src === "queue" || !jQuery._data(elem, queueDataKey) ); &&
( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
// 为硬编码回调提供空间,首先触发
// 最后标记/在元素上排队其他内容
setTimeout( function() {
if ( !jQuery._data( elem,queueDataKey ) &&
!jQuery._data( elem, markDataKey ) ) {
jQuery.removeData ( elem, deferDataKey, true );
defer.fire()
}
}, 0 );
}
}
jQuery.extend({
_mark: function( elem, type ) {
if ( elem ) {
type = ( type || "fx" ) "mark";
jQuery._data( elem, type, (jQuery._data( elem,类型 ) || 0) 1 );
}
},
_unmark: function(force, elem, type ) {
if (force !== true ) {
type = elem ;
元素=力
力=假;
}
if ( elem ) {
type = type || “fx”;
var key = 输入“mark”,
count = 力? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
if ( count ) {
jQuery._data( elem, key, count );
} else {
jQuery.removeData( elem, key, true );
handleQueueMarkDefer( elem, type, "mark" );
}
}
},
队列: function( elem, type, data ) {
var q;
if ( elem ) {
type = ( type || "fx" ) "queue";
q = jQuery._data( elem, 类型 );
// 如果这只是一个查找,则通过快速退出来加速出队
if ( data ) {
if ( !q || jQuery.isArray(data) ) {
q = jQuery. _data( elem, 类型, jQuery.makeArray(data) );
} else {
q.push( 数据 );
}
}
返回q || [];
}
},
出队: function( elem, type ) {
type = type || “fx”;
var queue = jQuery.queue( elem, type ),
fn = queue.shift(),
hooks = {};
// 如果 fx 队列出队,总是删除进度标记
if ( fn === "inprogress" ) {
fn = queue.shift();
}
if ( fn ) {
// 添加进度哨兵,防止 fx 队列被
// 自动出列
if ( type === "fx" ) {
queue.unshift(“进行中”);
}
jQuery._data( elem, type ".run", hooks );
fn.call( elem, function() {
jQuery.dequeue( elem, type );
}, hooks );
}
if ( !queue.length ) {
jQuery.removeData( elem, type "queue " type ".run", true );
handleQueueMarkDefer( elem, type, "queue" );
}
}
});
jQuery.fn.extend({
queue: function( type, data ) {
var setter = 2;
if ( typeof type !== "string" ) {
data = type;
type = "fx";
setter --
}
if (args.length
}
返回数据 === 未定义
this :
this.each(function() {
var queue = jQuery.queue( this, type, data );
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue( this, type );
}
});
出队: function( type ) {
return this.each(function() {
jQuery.dequeue( this, type );
});
},
//基于 Clint Helfers 的插件,经许可
// http://blindsignals.com/index.php/2009/07/jquery-delay/
delay: function( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
type = type || "fx"; {
var timeout = setTimeout( next, time );
hooks.stop = function() {
clearTimeout( timeout );
}); 🎜>clearQueue: function( type ) {
return this.queue( type || “fx”,[]);
},
// 当某种类型的队列被清空时,得到一个已解决的 Promise
// (fx 是默认类型)
promise: function( type, object ) {
if ( typeof type !== "string" ) {
object = type;
类型=未定义;
}
类型 = 类型 || “fx”;
var defer = jQuery.Deferred(),
elements = this,
i = elements.length,
count = 1,
deferDataKey = type "defer",
queueDataKey =输入“队列”,
markDataKey = 输入“标记”,
tmp;
函数resolve() {
if ( !( --count ) ) {
defer.resolveWith( elements, [ elements ] );
}
}
while( i-- ) {
if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
( jQuery.data ( elements[ i ],queueDataKey, undefined, true ) ||
jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
jQuery.data( elements[ i ], deferDataKey, jQuery. Callbacks( "一次记忆" ), true ) )) {
count ;
tmp.add( 解析 );
}
}
resolve();
返回 defer.promise( 对象 );
}
});
})( jQuery );
这时候,钩子机制其实已经在jQuery内部蔓延起来,1.5是css模块的cssHooks,1.6是属性模块的attrHooks, propHooks, boolHooks, nodeHooks,1.7是事件模块的fixHooks, keyHooks, mouseHooks,1.8是queue模块的_queueHooks,由于_queueHooks,queue终于瘦身了。
View Code?//1.8
jQuery.extend({
queue: function( elem, type, data ) {
var queue;
if ( elem ) {
type = ( type || "fx" ) "queue";
queue = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
if ( !queue || jQuery.isArray(data) ) {
queue = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
queue.push( data );
}
}
return queue || [];
}
},
dequeue: function( elem, type ) {
type = type || "fx";
var queue = jQuery.queue( elem, type ),
fn = queue.shift(),
hooks = jQuery._queueHooks( elem, type ),
next = function() {
jQuery.dequeue( elem, type );
};
// If the fx queue is dequeued, always remove the progress sentinel
if ( fn === "inprogress" ) {
fn = queue.shift();
}
if ( fn ) {
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
queue.unshift( "inprogress" );
}
// clear up the last queue stop function
delete hooks.stop;
fn.call( elem, next, hooks );
}
if ( !queue.length && hooks ) {
hooks.empty.fire();
}
},
// not intended for public consumption - generates a queueHooks object, or returns the current one
_queueHooks: function( elem, type ) {
var key = type "queueHooks";
return jQuery._data( elem, key ) || jQuery._data( elem, key, {
empty: jQuery.Callbacks("once memory").add(function() {
jQuery.removeData( elem, type "queue", true );
jQuery.removeData( elem, key, true );
})
});
}
});
jQuery.fn.extend({
queue: function( type, data ) {
var setter = 2;
if ( typeof type !== "string" ) {
data = type;
type = "fx";
setter--;
}
if ( arguments.length return jQuery.queue( this[0], type );
}
return data === undefined ?
this :
this.each(function() {
var queue = jQuery.queue( this, type, data );
// ensure a hooks for this queue
jQuery._queueHooks( this, type );
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue( this, type );
}
});
},
dequeue: function( type ) {
return this.each(function() {
jQuery.dequeue( this, type );
});
},
// Based off of the plugin by Clint Helfers, with permission.
// http://blindsignals.com/index.php/2009/07/jquery-delay/
delay: function( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
type = type || "fx";
return this.queue( type, function( next, hooks ) {
var timeout = setTimeout( next, time );
hooks.stop = function() {
clearTimeout( timeout );
};
});
},
clearQueue: function( type ) {
return this.queue( type || "fx", [] );
},
// Get a promise resolved when queues of a certain type
// are emptied (fx is the type by default)
promise: function( type, obj ) {
var tmp,
count = 1,
defer = jQuery.Deferred(),
elements = this,
i = this.length,
resolve = function() {
if ( !( --count ) ) {
defer.resolveWith( elements, [ elements ] );
}
};
if ( typeof type !== "string" ) {
obj = type;
type = undefined;
}
type = type || "fx";
while( i-- ) {
if ( (tmp = jQuery._data( elements[ i ], type "queueHooks" )) && tmp.empty ) {
count ;
tmp.empty.add( resolve );
}
}
resolve();
return defer.promise( obj );
}
});
同时,动画模块迎来了它第三次大重构,它也有一个钩子Tween.propHooks。它多出两个对象,其中Animation返回一个异步列队,Tween 是用于处理单个样式或属性的变化,相当于之前Fx对象。animate被抽空了,它在1.72可是近百行的规模。jQuery通过钩子机制与分化出一些新的对象,将一些巨型方法重构掉。现在非常长的方法只龟缩在节点模块,回调模块。
animate: function( prop, speed, easing, callback ) {
var empty = jQuery.isEmptyObject( prop ),
optall = jQuery.speed( speed, easing, callback ),
doAnimation = function() {
// Operate on a copy of prop so per-property easing won't be lost
var anim = Animation( this, jQuery.extend( {}, prop ), optall );
// Empty animations resolve immediately
if ( empty ) {
anim.stop( true );
}
};
return empty || optall.queue === false ?
this.each( doAnimation ) :
this.queue( optall.queue, doAnimation );
},
到目前为止,所有异步的东西都被jQuery改造成异步列队的“子类”或叫“变种”更合适些。如domReady, 动画,AJAX,与执行了promise或delay或各种特效方法之后的jQuery对象。于是所有异步的东西在promise的加护下,像同步那样编写异步程序。

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

JavaScript在现实世界中的应用包括服务器端编程、移动应用开发和物联网控制:1.通过Node.js实现服务器端编程,适用于高并发请求处理。2.通过ReactNative进行移动应用开发,支持跨平台部署。3.通过Johnny-Five库用于物联网设备控制,适用于硬件交互。

我使用您的日常技术工具构建了功能性的多租户SaaS应用程序(一个Edtech应用程序),您可以做同样的事情。 首先,什么是多租户SaaS应用程序? 多租户SaaS应用程序可让您从唱歌中为多个客户提供服务


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

WebStorm Mac版
好用的JavaScript开发工具

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

Atom编辑器mac版下载
最流行的的开源编辑器