jQuery 队列实现原理
理解 jQuery 队列机制并手写实现
问题
jQuery 的队列是如何实现的?如何手写一个简化版的队列系统?
解答
jQuery 队列本质是一个函数数组,通过 queue() 添加函数,dequeue() 取出并执行函数。主要用于动画的顺序执行。
jQuery 队列的基本用法
// 向队列添加函数
$('#box').queue('fx', function(next) {
console.log('第一个任务');
next(); // 必须调用 next 才会执行下一个
});
$('#box').queue('fx', function(next) {
console.log('第二个任务');
next();
});
// 开始执行队列
$('#box').dequeue('fx');
手写实现
class Queue {
constructor() {
// 存储不同命名空间的队列
this.queues = {};
}
// 添加函数到队列
queue(name, fn) {
// 初始化队列数组
if (!this.queues[name]) {
this.queues[name] = [];
}
// 如果传入函数,添加到队列
if (typeof fn === 'function') {
this.queues[name].push(fn);
}
// 返回当前队列(用于查看)
return this.queues[name];
}
// 取出并执行队列中的下一个函数
dequeue(name) {
const queue = this.queues[name];
if (!queue || queue.length === 0) {
return;
}
// 取出第一个函数
const fn = queue.shift();
// 创建 next 函数,用于执行下一个
const next = () => {
this.dequeue(name);
};
// 执行函数,传入 next
fn(next);
}
// 清空队列
clearQueue(name) {
if (this.queues[name]) {
this.queues[name] = [];
}
}
}
// 使用示例
const q = new Queue();
// 添加异步任务
q.queue('animate', function(next) {
console.log('任务1: 开始');
setTimeout(() => {
console.log('任务1: 完成');
next(); // 1秒后执行下一个
}, 1000);
});
q.queue('animate', function(next) {
console.log('任务2: 开始');
setTimeout(() => {
console.log('任务2: 完成');
next();
}, 500);
});
q.queue('animate', function(next) {
console.log('任务3: 同步完成');
next();
});
// 开始执行
q.dequeue('animate');
// 输出顺序:
// 任务1: 开始
// (1秒后) 任务1: 完成
// 任务2: 开始
// (0.5秒后) 任务2: 完成
// 任务3: 同步完成
模拟 jQuery 动画队列
class AnimationQueue {
constructor(element) {
this.element = element;
this.queue = [];
this.running = false;
}
// 添加动画到队列
animate(props, duration) {
this.queue.push({
type: 'animate',
props,
duration
});
this._run();
return this; // 链式调用
}
// 添加延迟
delay(ms) {
this.queue.push({
type: 'delay',
duration: ms
});
this._run();
return this;
}
// 执行队列
_run() {
if (this.running || this.queue.length === 0) {
return;
}
this.running = true;
const task = this.queue.shift();
if (task.type === 'delay') {
setTimeout(() => {
this.running = false;
this._run();
}, task.duration);
} else if (task.type === 'animate') {
// 简化的动画实现
console.log(`动画: ${JSON.stringify(task.props)}, 时长: ${task.duration}ms`);
setTimeout(() => {
this.running = false;
this._run();
}, task.duration);
}
}
// 停止并清空队列
stop() {
this.queue = [];
this.running = false;
return this;
}
}
// 使用
const anim = new AnimationQueue('#box');
anim
.animate({ left: 100 }, 500)
.delay(200)
.animate({ top: 100 }, 500)
.animate({ opacity: 0 }, 300);
关键点
- 队列是一个函数数组,遵循先进先出(FIFO)原则
queue()向数组末尾添加函数,dequeue()从数组头部取出并执行- 每个函数接收
next参数,必须调用它才能继续执行下一个任务 - 支持命名空间,jQuery 默认使用
fx作为动画队列名 - 异步任务通过
next()回调控制执行顺序,实现串行执行
目录