用Promise打造智能任务队列

你在电商大促时同时发起100个商品图片上传请求,服务器不堪重负直接崩溃------这就是并发控制的生死时刻!本文将用Promise打造一个智能任务队列,让异步任务像收费站车流一样高效运转。


为何需要任务队列?

当我们面对大量异步任务时:

javascript 复制代码
// 灾难现场:同时发起100个请求
for (let i = 0; i < 100; i++) {
  fetch(`/api/upload?img=product-${i}.jpg`)
}

痛点分析

  1. 服务器压力:瞬间100个并发请求可能压垮服务端
  2. 浏览器限制:Chrome最多允许6个同域名并发请求
  3. 资源竞争:过多任务同时执行导致内存/CPU暴涨

方案对比

方案 优势 劣势 适用场景
无控制直接并发 实现简单 易引发雪崩效应 少量安全请求
Promise.all 批量处理 无并发限制 小批量任务
Promise队列 精准流量控制 需额外实现 高并发场景

核心实现:智能限流控制器

1. 类结构设计 - 三个核心成员

javascript 复制代码
class TaskQueue {
  constructor(maxConcurrent) {
    this.maxConcurrent = maxConcurrent; // 最大并发数
    this.currentCount = 0;        // 当前进行中的任务数
    this.pendingQueue = [];       // 等待队列
  }

  // 添加任务到队列(核心方法)
  enqueue(taskFunc) {
    // 实现见下文
  }
}

2. 任务调度逻辑 - 收费站模型

javascript 复制代码
enqueue(taskFunc) {
  return new Promise((resolve, reject) => {
    // 封装原始任务(添加执行上下文)
    const wrappedTask = async () => {
      try {
        this.currentCount++;
        const result = await taskFunc(); // 执行实际任务
        resolve(result); // 传递结果
      } catch (error) {
        reject(error); // 传递错误
      } finally {
        this.currentCount--;
        this._runNext(); // 完成后触发下个任务
      }
    };

    // 决策逻辑:立即执行 or 加入等待
    if (this.currentCount < this.maxConcurrent) {
      wrappedTask();
    } else {
      this.pendingQueue.push(wrappedTask);
    }
  });
}

// 执行队列中的下一个任务
_runNext() {
  if (this.pendingQueue.length === 0) return;
  if (this.currentCount >= this.maxConcurrent) return;
  
  const nextTask = this.pendingQueue.shift();
  nextTask();
}

实战场景:电商图片上传系统

1. 模拟上传函数

javascript 复制代码
// 模拟带随机失败率的图片上传
const mockUpload = (id) => () => 
  new Promise((resolve, reject) => {
    const delay = 500 + Math.random() * 1000;
    setTimeout(() => {
      Math.random() > 0.2 
        ? resolve(`图片${id}上传成功`) 
        : reject(`图片${id}上传超时`);
    }, delay);
  });

2. 队列化上传 - 安全执行

javascript 复制代码
const uploadQueue = new TaskQueue(3); // 最大并发3

// 批量添加15个上传任务
const tasks = [];
for (let i = 1; i <= 15; i++) {
  tasks.push(uploadQueue.enqueue(mockUpload(i)));
}

// 全局结果处理
Promise.allSettled(tasks).then(results => {
  const success = results.filter(r => r.status === 'fulfilled');
  console.log(`成功率: ${success.length}/${results.length}`);
});

3. 执行效果

bash 复制代码
# 控制台输出示例
[执行中] 图片1 图片2 图片3
[完成] 图片1 (成功)
[开始] 图片4 (从队列补位)
[失败] 图片2 (超时)
[开始] 图片5 (继续补位)
...
最终统计:成功率: 12/15

关键优势

  • 流量平滑:始终维持≤3个并行上传
  • 错误隔离:单个失败不影响整体流程
  • 顺序保证:先进先出(FIFO)执行机制

性能优化:高级特性增强

1. 优先级队列 - VIP任务插队

javascript 复制代码
enqueue(taskFunc, priority = 0) {
  // ...(省略基础逻辑)
  const taskWrapper = { run: wrappedTask, priority };
  
  // 优先级插队逻辑
  if (this.currentCount >= this.maxConcurrent) {
    const index = this.pendingQueue.findIndex(
      t => priority > t.priority
    );
    index === -1 
      ? this.pendingQueue.push(taskWrapper)
      : this.pendingQueue.splice(index, 0, taskWrapper);
  }
}

// 使用示例 - 设置优先上传VIP商品图片
uploadQueue.enqueue(mockUpload('VIP'), 1); // 优先级高于默认0

2. 动态扩容 - 智能调整并发数

javascript 复制代码
// 根据网络状况自动调整
setConcurrent(newMax) {
  this.maxConcurrent = newMax;
  
  // 扩容时立即触发等待任务
  while (
    this.currentCount < this.maxConcurrent && 
    this.pendingQueue.length > 0
  ) {
    const task = this.pendingQueue.shift();
    task.run();
  }
}

方案对比:三种进阶实现方式

实现方案 实现复杂度 调度精度 功能扩展性 适用场景
基础Promise队列 ★★☆ ★★★ ★★☆ 简单并发控制
Priority Queue ★★★ ★★★ ★★★ 优先级任务系统
AsyncPool ★★☆ ★★☆ ★★☆ 固定池资源管理

选型黄金法则

  • 中小项目直接用基础Promise队列
  • 复杂系统选择Priority Queue方案
  • Node.js I/O密集型用AsyncPool

代码实现(完整版)

javascript 复制代码
class SmartTaskQueue {
  constructor(maxConcurrent = 5) {
    this.maxConcurrent = maxConcurrent;
    this.activeCount = 0;
    this.queue = [];
  }

  enqueue(task, priority = 0) {
    return new Promise((resolve, reject) => {
      const taskWrapper = async () => {
        this.activeCount++;
        try {
          const result = await task();
          resolve(result);
        } catch (error) {
          reject(error);
        } finally {
          this.activeCount--;
          this._dequeue();
        }
      };

      if (this.activeCount < this.maxConcurrent) {
        taskWrapper();
      } else {
        this.queue.push({ task: taskWrapper, priority });
        this.queue.sort((a, b) => b.priority - a.priority); // 优先级排序
      }
    });
  }

  _dequeue() {
    if (this.queue.length === 0) return;
    if (this.activeCount >= this.maxConcurrent) return;

    const nextTask = this.queue.shift().task;
    nextTask();
  }

  resize(newSize) {
    this.maxConcurrent = newSize;
    while (
      this.activeCount < this.maxConcurrent && 
      this.queue.length > 0
    ) {
      this._dequeue();
    }
  }
}

何时使用任务队列?

  1. 网络请求:API调用、文件上传/下载
  2. 资源加载:图片懒加载、分块加载大文件
  3. 硬件交互:蓝牙/串口设备指令发送
  4. 批量操作:数据库写入、日志上传

实战技巧:配合Web Worker使用可避免阻塞主线程

javascript 复制代码
// 在Worker中使用任务队列
const worker = new Worker('task-worker.js');
worker.postMessage({ type: 'INIT_QUEUE', maxConcurrent: 4 });
相关推荐
一只小风华~1 小时前
Web前端:JavaScript和CSS实现的基础登录验证功能
前端
90后的晨仔1 小时前
Vue Router 入门指南:从零开始实现前端路由管理
前端·vue.js
LotteChar1 小时前
WebStorm vs VSCode:前端圈的「豆腐脑甜咸之争」
前端·vscode·webstorm
90后的晨仔1 小时前
零基础快速搭建 Vue 3 开发环境(附官方推荐方法)
前端·vue.js
洛_尘1 小时前
Java EE进阶2:前端 HTML+CSS+JavaScript
java·前端·java-ee
孤独的根号_2 小时前
Vite背后的技术原理🚀:为什么选择Vite作为你的前端构建工具💥
前端·vue.js·vite
吹牛不交税2 小时前
Axure RP Extension for Chrome插件安装使用
前端·chrome·axure
薛定谔的算法2 小时前
# 前端路由进化史:从白屏到丝滑体验的技术突围
前端·react.js·前端框架
拾光拾趣录3 小时前
Element Plus表格表头动态刷新难题:零闪动更新方案
前端·vue.js·element
Adolf_19934 小时前
React 中 props 的最常用用法精选+useContext
前端·javascript·react.js