原来 JavaScript 也可以写队列
我原本以为 JavaScript 不能写队列,但最近发现 JavaScript 可以模拟队列,并且有很多不同的实现方式。
场景
场景一
有几百条数据需要传递给后端,但后端每次最多只能接收 10 条数据。
场景二
在弹出第一个弹框并关闭后再弹第二个弹窗(或者广告)。
这些需求大家应该都了解过,或者遇到过。我们可能第一反应是通过定时器或者 Promise 来处理,但其实完全可以通过队列的方式来处理。下面我会写一些示例,大家可以根据自己的需求进行修改。
说明
JavaScript 队列(Queue)是一种先进先出(FIFO)的数据结构,用于存储按顺序排列的元素,并支持插入新元素和删除元素的操作。队列在计算机科学中有着广泛的应用,例如在操作系统中实现任务调度、在数据压缩中使用哈夫曼编码等。在 JavaScript 中,队列也常用于实现异步编程和控制流程。
JavaScript 队列通常可以通过内置的 Array 对象实现,或者使用第三方库如 queue-lib
。Array 对象提供了 push()
和 shift()
方法,可以模拟队列的入队和出队操作。
简单队列
下面是一个简单的请求队列示例:
class RequestQueue {
constructor() {
this.queue = []; // 请求队列
this.running = 0; // 当前正在运行的请求数量
}
// 将请求添加到队列中
enqueue(request) {
this.queue.push(request);
this.run(); // 检查是否可以运行新的请求
}
// 检查并运行请求
run() {
if (this.running !== 0 || !this.queue.length) return;
const request = this.queue.shift();
this.execute(request);
this.running++;
}
// 执行请求
execute(request) {
request().finally(() => {
this.running--;
if (this.queue.length <= 0) return;
this.run();
});
}
}
// 模拟一个异步请求
function makeRequest(id) {
return new Promise((resolve) => {
console.log(`正在发送请求 ${id}`);
setTimeout(() => {
console.log(`请求 ${id} 完成`);
resolve(id);
}, 1000);
});
}
// 创建请求队列并添加 10 个请求
const queue = new RequestQueue();
for (let i = 1; i <= 10; i++) {
queue.enqueue(async () => await makeRequest(i));
}
完整队列
我们可以控制并发量以及请求间隔时间。下面是一个更完整的实现:
class RequestQueue {
constructor(limit, delay) {
this.limit = limit; // 最大并发请求数
this.delay = delay; // 批次间隔时间(毫秒)
this.queue = []; // 请求队列
this.running = 0; // 当前正在运行的请求数量
}
enqueue(request) {
this.queue.push(request);
this.run(); // 检查是否可以运行新的请求
}
run() {
while (this.running < this.limit && this.queue.length > 0) {
const request = this.queue.shift();
this.execute(request);
this.running++;
}
}
execute(request) {
request().finally(() => {
this.running--;
if (this.queue.length > 0) {
setTimeout(() => {
this.run();
}, this.delay);
}
});
}
}
// 模拟一个异步请求
function makeRequest(id) {
return new Promise((resolve) => {
console.log(`发起请求 ${id}`);
setTimeout(() => {
console.log(`请求 ${id} 完成`);
resolve(id);
}, 100);
});
}
// 创建请求队列并添加 10,000 个请求
const queue = new RequestQueue(5, 1000);
for (let i = 1; i <= 20; i++) {
queue.enqueue(async () => await makeRequest(i));
}
补充完整队列
我们可以增加一个方法来监听队列是否完成:
class RequestQueue {
timer = null; // 定时器
constructor(limit, delay) {
this.limit = limit; // 最大并发请求数
this.delay = delay; // 批次间隔时间(毫秒)
this.queue = []; // 请求队列
this.running = 0; // 当前正在运行的请求数量
}
enqueue(request) {
this.queue.push(request);
this.run(); // 检查是否可以运行新的请求
}
run() {
while (this.running < this.limit && this.queue.length > 0) {
const request = this.queue.shift();
this.execute(request);
this.running++;
}
}
execute(request) {
request().finally(() => {
this.running--;
if (this.queue.length > 0) {
setTimeout(() => {
this.run();
}, this.delay);
}
});
}
isQueueEmpty() {
return new Promise((resolve) => {
this.timer = setInterval(() => {
if (this.queue.length === 0 && this.running === 0) {
clearInterval(this.timer);
this.timer = null;
resolve(true);
}
}, 1000);
});
}
}
// 模拟一个异步请求
function makeRequest(id) {
return new Promise((resolve) => {
console.log(`发起请求 ${id}`);
setTimeout(() => {
console.log(`请求 ${id} 完成`);
resolve(id);
}, 100);
});
}
// 创建请求队列并添加 10,000 个请求
const queue = new RequestQueue(5, 1000);
for (let i = 1; i <= 20; i++) {
queue.enqueue(async () => await makeRequest(i));
}
// 执行完成返回 true
queue.isQueueEmpty().then(res => {
console.log(res);
});
总结
以上就是 JavaScript 模拟队列的基础写法和用法,大家可以根据需要进行修改和完善。赶紧打开 VSCode 内卷起来吧!