读jQuery之二十一(队列queue)
queue模块在jQuery中分在Effects中,搜索整个库会发现queue也仅在特效模块effects.js中被使用。jQuery抽取出独立的命名空间给queue,说明除了内部Effects模块使用外,客户端程序员可以充分发挥聪明才智使用queue来构建非动画API。
queue模块向外开放的API分别是
- 挂在.queue、._queueHooks(仅内部)
- 挂在jQuery对象上的有queue、dequeue、delay、clearQueue、promise
按照jQuery的惯例,挂在$上的方法属于低级API,挂在jQuery对象上的才是经常使用的。
一般低级API是为高级API服务的,即 queue内部会使用.dequeue。这里实际是实现为一个队列,.dequeue是出列。
一、$.queue
这个方法有两个作用,它既是setter,又是getter。第一个参数elem是DOM元素,第二个参数type是字符串,第三个参数data可以是function或数组。
1. 设置指定名字的queue
1 2 3 4 5 6 | function cb1() {alert(1)} function cb2() {alert(2)} var arr = [cb1, cb2]; $.queue(el, 'mx' , cb1); // 第三个参数为function $.queue(el, 'xm' , arr); // 第三个参数为数组 |
2. 这时可以取到存入的callbacks
1 2 | var cbs1 = $.queue(el, 'mx' ); // [cb1] var cbs2 = $.queue(el, 'xm' ); // [cb1, cb2] |
._data方法,将数据保存下来。默认type/queueName使用 "fx" + queue。$.queue的实现很简单,代码不过15行,即取缓存对象queue,如果不存在则初始化为一个空对象,然后将data存入,如果存在则直接将data push到数组中。
二、$.dequeue
将回调函数出列执行,每调用一次仅出列一个,因此当回调有N个时,需要调用.dequeue的第一个参数是dom元素,第二个参数是queueName
1 2 3 4 5 6 7 8 9 10 11 12 13 | function ff1() {console.log(1)} function ff2() {console.log(2)} function ff3() {console.log(3)} var p = $( 'p' )[0]; $.queue(p, 'mx1' , ff1); $.queue(p, 'mx1' , ff2); $.queue(p, 'mx1' , ff3); // 每2秒调用一次$.dequeue,依次输出1,2,3 setInterval( function () { $.dequeue(p, 'mx1' ) }, 2000); |
回调函数的上下文是dom元素,参数是next函数和hooks对象
1 2 3 4 5 6 7 8 | var p = $( 'p' )[0]; function func(next, hooks) { console.log( this ); console.log(next); console.log(hooks); } $.queue(p, 'mx' , func); $.dequeue(p, 'mx' ); // p, function, [object Object] |
next内部仍然调用.dequeue里的hooks是当队列里所有的callback都执行完后(此时startLength为0)进行最后的一个清理工作,
1 2 3 | if ( !startLength && hooks ) { hooks.empty.fire(); } |
hooks.empty是一个jQuery.Callbacks对象,而它则是定义在$._queueHooks里
1 2 3 4 5 6 7 8 9 | _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" ); jQuery._removeData( elem, key ); }) }); } |
以上就是queue的全部了,本质是利用Array的push和shift来完成先进先出(First In First Out),但这里有个缺陷,jQuery的queue从1.1开始就是为effects模块服务的,因此queue里存的都是function。个人觉得如果只存function,应该对data参数做个严格类型判断,如果不是function则抛异常。但目前的版本没有做严格判断,如果我存的不是function,这样dequeue时会报错。如下
1 2 3 | var p = $( 'p' )[0]; $.queue(p, 'mx1' , {}); // 注意第三个参数是对象,非function $.dequeue(p, 'mx1' ); // fn.call 报错,因为fn不是function |
三、queue
知道了.queue。queue比$.queue 少了第一个参数,内部使用this代替第一个参数。
1 2 3 4 5 6 7 8 | function ff1() {console.log(1)} function ff2() {console.log(2)} function ff3() {console.log(3)} var $p = $( 'p' ); $p.queue( 'mx' , ff1); $p.queue( 'mx' , ff2); $p.queue( 'mx' , ff3); |
这样,三个function就入列了,列名是"mx"。 取队列元素只需传一个列名如"mx"
1 | var queue = $p.queue( 'mx' ); // [ff1, ff2, ff3] |
还有个技巧就是,如果使用jQuery默认的队列"fx",可以只传data
1 2 3 4 5 6 7 | function ff1() {console.log(1)} function ff2() {console.log(2)} function ff3() {console.log(3)} var $p = $( 'p' ); $p.queue(ff1); $p.queue(ff2); $p.queue(ff3); |
另外一点,当使用默认列名"fx"时,它会调用$.dequeue出列执行下,源码如下
1 2 3 | if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this , type ); } |
四、dequeue
dequeue则更是未添加任何特殊处理,直接调用的$.dequeue,见源码
1 2 3 4 5 | dequeue: function ( type ) { return this .each( function () { jQuery.dequeue( this , type ); }); }, |
五、delay
delay用来延迟后续添加的callback的执行,第一个参数time是延迟时间(另可使用"slow"和"fast"),第二个是队列名。
1 2 3 4 5 6 7 | function cb() { console.log(1); } var $p = $( 'p' ); $p.delay(2000, 'mx' ).queue( 'mx' , cb); $p.dequeue( 'mx' ); // 2秒后输出1 |
如果是这样
1 2 3 4 5 6 7 8 9 10 | function ff1() {console.log(1)} function ff2() {console.log(2)} var $p = $( 'p' ); $p.queue( 'mx' , ff1); $p.delay(4000, 'mx' ); $p.queue( 'mx' , ff2); $p.dequeue( 'mx' ); // 立即输出1 $p.dequeue( 'mx' ); // 4秒后输出2 |
六、clearQueue
顾名思义,清空所有队列。没什么好说的,源码如下,直接使用一个空数组覆盖之前的数组队列了。
1 2 3 | clearQueue: function ( type ) { return this .queue( type || "fx" , [] ); }, |
七、promise
这个方法返回一个promise对象,promise对象既是前面提到的Deferred对象的阉割版。你可以使用done、fail、progress添加,但不能触发。用在queue模块里有特殊意义,比如done它指queue里所有function都执行后才执行done添加的。如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | function ff1() { alert(1) } function ff2() { alert(2) } function succ() { alert( 'done' ) } $body = $( 'body' ) $body.queue( 'mx' , ff1); $body.queue( 'mx' , ff2); var promise = $body.promise( 'mx' ); promise.done(succ); setInterval( function () { $body.dequeue( 'mx' ) // 先弹出1,2,最后是"done" }, 1500) |
注:阅读版本为1.9.1
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端